shithub: jbig2

Download patch

ref: 971100aa3b5f56756fbd31a03ca4212209881f09
parent: 98a2ac892b58569ae1a5a258c0b502fbf56da0b4
author: raph <raph@ded80894-8fb9-0310-811b-c03f3676ab4d>
date: Thu Apr 25 19:24:08 EDT 2002

Adds partial support for mmr (CCITT 2D fax) decoding. This commit can
scan Huffman codes, but does not apply the logic to produce run lengths.


git-svn-id: http://svn.ghostscript.com/jbig2dec/trunk@48 ded80894-8fb9-0310-811b-c03f3676ab4d

--- a/jbig2_generic.c
+++ b/jbig2_generic.c
@@ -8,7 +8,7 @@
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
         
-    $Id: jbig2_generic.c,v 1.3 2002/02/19 07:09:16 giles Exp $
+    $Id: jbig2_generic.c,v 1.4 2002/04/25 23:24:08 raph Exp $
 */
 
 /**
@@ -26,6 +26,7 @@
 #include "jbig2_priv.h"
 #include "jbig2_arith.h"
 #include "jbig2_generic.h"
+#include "jbig2_mmr.h"
 
 typedef struct {
   int32_t width;
@@ -171,19 +172,157 @@
   return 0;
 }
 
+static int
+jbig2_decode_generic_template2(Jbig2Ctx *ctx,
+			       int32_t seg_number,
+			       const Jbig2GenericRegionParams *params,
+			       Jbig2ArithState *as,
+			       byte *gbreg,
+			       Jbig2ArithCx *GB_stats)
+{
+  int GBW = params->GBW;
+  int rowstride = (GBW + 7) >> 3;
+  int x, y;
+  byte *gbreg_line = gbreg;
+  bool LTP = 0;
+
+  /* todo: currently we only handle the nominal gbat location */
+
+#ifdef OUTPUT_PBM
+  printf("P4\n%d %d\n", GBW, params->GBH);
+#endif
+
+  for (y = 0; y < params->GBH; y++)
+    {
+      uint32_t CONTEXT;
+      uint32_t line_m1;
+      uint32_t line_m2;
+      int padded_width = (GBW + 7) & -8;
+
+      line_m1 = (y >= 1) ? gbreg_line[-rowstride] : 0;
+      line_m2 = (y >= 2) ? gbreg_line[-(rowstride << 1)] << 4 : 0;
+      CONTEXT = ((line_m1 >> 3) & 0x7c) | ((line_m2 >> 3) & 0x380);
+
+      /* 6.2.5.7 3d */
+      for (x = 0; x < padded_width; x += 8)
+	{
+	  byte result = 0;
+	  int x_minor;
+	  int minor_width = GBW - x > 8 ? 8 : GBW - x;
+
+	  if (y >= 1)
+	    line_m1 = (line_m1 << 8) |
+	      (x + 8 < GBW ? gbreg_line[-rowstride + (x >> 3) + 1] : 0);
+
+	  if (y >= 2)
+	    line_m2 = (line_m2 << 8) |
+	      (x + 8 < GBW ? gbreg_line[-(rowstride << 1) + (x >> 3) + 1] << 4: 0);
+
+	  /* This is the speed-critical inner loop. */
+	  for (x_minor = 0; x_minor < minor_width; x_minor++)
+	    {
+	      bool bit;
+
+	      bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]);
+	      result |= bit << (7 - x_minor);
+	      CONTEXT = ((CONTEXT & 0x1bd) << 1) | bit |
+		((line_m1 >> (10 - x_minor)) & 0x4) |
+		((line_m2 >> (10 - x_minor)) & 0x80);
+	    }
+	  gbreg_line[x >> 3] = result;
+	}
+#ifdef OUTPUT_PBM
+      fwrite(gbreg_line, 1, rowstride, stdout);
+#endif
+      gbreg_line += rowstride;
+    }
+
+  return 0;
+}
+
+static int
+jbig2_decode_generic_template2a(Jbig2Ctx *ctx,
+			       int32_t seg_number,
+			       const Jbig2GenericRegionParams *params,
+			       Jbig2ArithState *as,
+			       byte *gbreg,
+			       Jbig2ArithCx *GB_stats)
+{
+  int GBW = params->GBW;
+  int rowstride = (GBW + 7) >> 3;
+  int x, y;
+  byte *gbreg_line = gbreg;
+  bool LTP = 0;
+
+  /* This is a special case for GBATX1 = 3, GBATY1 = -1 */
+
+#ifdef OUTPUT_PBM
+  printf("P4\n%d %d\n", GBW, params->GBH);
+#endif
+
+  for (y = 0; y < params->GBH; y++)
+    {
+      uint32_t CONTEXT;
+      uint32_t line_m1;
+      uint32_t line_m2;
+      int padded_width = (GBW + 7) & -8;
+
+      line_m1 = (y >= 1) ? gbreg_line[-rowstride] : 0;
+      line_m2 = (y >= 2) ? gbreg_line[-(rowstride << 1)] << 4 : 0;
+      CONTEXT = ((line_m1 >> 3) & 0x78) | ((line_m1 >> 2) & 0x4) | ((line_m2 >> 3) & 0x380);
+
+      /* 6.2.5.7 3d */
+      for (x = 0; x < padded_width; x += 8)
+	{
+	  byte result = 0;
+	  int x_minor;
+	  int minor_width = GBW - x > 8 ? 8 : GBW - x;
+
+	  if (y >= 1)
+	    line_m1 = (line_m1 << 8) |
+	      (x + 8 < GBW ? gbreg_line[-rowstride + (x >> 3) + 1] : 0);
+
+	  if (y >= 2)
+	    line_m2 = (line_m2 << 8) |
+	      (x + 8 < GBW ? gbreg_line[-(rowstride << 1) + (x >> 3) + 1] << 4: 0);
+
+	  /* This is the speed-critical inner loop. */
+	  for (x_minor = 0; x_minor < minor_width; x_minor++)
+	    {
+	      bool bit;
+
+	      bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]);
+	      result |= bit << (7 - x_minor);
+	      CONTEXT = ((CONTEXT & 0x1b9) << 1) | bit |
+		((line_m1 >> (10 - x_minor)) & 0x8) |
+		((line_m1 >> (9 - x_minor)) & 0x4) |
+		((line_m2 >> (10 - x_minor)) & 0x80);
+	    }
+	  gbreg_line[x >> 3] = result;
+	}
+#ifdef OUTPUT_PBM
+      fwrite(gbreg_line, 1, rowstride, stdout);
+#endif
+      gbreg_line += rowstride;
+    }
+
+  return 0;
+}
+
 /**
  * jbig2_decode_generic_region: Decode a generic region.
  * @ctx: The context for allocation and error reporting.
  * @params: Parameters, as specified in Table 2.
- * @data: The input data.
- * @size: The size of the input data, in bytes.
+ * @as: Arithmetic decoder state.
  * @gbreg: Where to store the decoded data.
+ * @GB_stats: Arithmetic stats.
  *
  * Decodes a generic region, according to section 6.2. The caller should
  * have allocated the memory for @gbreg, which is packed 8 pixels to a
  * byte, scanlines aligned to one byte boundaries.
  *
- * Todo: I think the stats need to be an argument.
+ * Because this API is based on an arithmetic decoding state, it is
+ * not suitable for MMR decoding.
  *
  * Return code: 0 on success.
  **/
@@ -201,6 +340,15 @@
   else if (!params->MMR && params->GBTEMPLATE == 1)
     return jbig2_decode_generic_template1(ctx, seg_number,
 					  params, as, gbreg, GB_stats);
+  else if (!params->MMR && params->GBTEMPLATE == 2)
+    {
+      if (params->gbat[0] == 3 && params->gbat[1] == 255)
+	return jbig2_decode_generic_template2a(ctx, seg_number,
+					       params, as, gbreg, GB_stats);
+      else
+	return jbig2_decode_generic_template2(ctx, seg_number,
+					       params, as, gbreg, GB_stats);
+    }
   {
     int i;
     for (i = 0; i < 8; i++)
@@ -284,21 +432,27 @@
 
   gbreg = jbig2_alloc(ctx->allocator, ((rsi.width + 7) >> 3) * rsi.height);
 
-  ws = jbig2_word_stream_buf_new(ctx,
-				 segment_data + offset,
-				 sh->data_length - offset);
-  as = jbig2_arith_new(ctx, ws);
 
-  if (!params.MMR)
+  if (params.MMR)
     {
+      code = jbig2_decode_generic_mmr(ctx, sh->segment_number, &params,
+				      segment_data + offset, sh->data_length - offset,
+				      gbreg);
+    }
+  else
+    {
       int stats_size = params.GBTEMPLATE == 0 ? 65536 :
 	params.GBTEMPLATE == 1 ? 8192 : 1024;
       GB_stats = jbig2_alloc(ctx->allocator, stats_size);
       memset(GB_stats, 0, stats_size);
-    }
 
-  code = jbig2_decode_generic_region(ctx, sh->segment_number, &params,
-				     as, gbreg, GB_stats);
+      ws = jbig2_word_stream_buf_new(ctx,
+				     segment_data + offset,
+				     sh->data_length - offset);
+      as = jbig2_arith_new(ctx, ws);
+      code = jbig2_decode_generic_region(ctx, sh->segment_number, &params,
+					 as, gbreg, GB_stats);
+    }
 
   /* todo: stash gbreg as segment result */
   /* todo: free ws, as */
--- /dev/null
+++ b/jbig2_mmr.c
@@ -1,0 +1,812 @@
+/* An implementation of MMR decoding. This is based on the implementation
+   in Ghostscript.
+*/
+
+#include <stdint.h>
+#include <stddef.h>
+#ifdef OUTPUT_PBM
+#include <stdio.h>
+#endif
+#include "jbig2.h"
+#include "jbig2_priv.h"
+#include "jbig2_arith.h"
+#include "jbig2_generic.h"
+#include "jbig2_mmr.h"
+
+typedef struct {
+  int width;
+  int height;
+  const byte *data;
+  size_t size;
+  int data_index;
+  int bit_index;
+  uint32_t word;
+} Jbig2MmrCtx;
+
+
+static void
+jbig2_decode_mmr_init(Jbig2MmrCtx *mmr, int width, int height,
+		      const byte *data, size_t size)
+{
+  int i;
+  uint32_t word = 0;
+
+  mmr->width = width;
+  mmr->height = height;
+  mmr->data = data;
+  mmr->size = size;
+  mmr->data_index = 0;
+  mmr->bit_index = 0;
+  for (i = 0; i < size && i < 4; i++)
+    word |= (data[i] << ((3 - i) << 3));
+  mmr->word = word;
+
+}
+
+static void
+jbig2_decode_mmr_consume(Jbig2MmrCtx *mmr, int n_bits)
+{
+  mmr->word <<= n_bits;
+  mmr->bit_index += n_bits;
+  while (mmr->bit_index >= 8)
+    {
+      mmr->bit_index -= 8;
+      if (mmr->data_index + 4 < mmr->size)
+	mmr->word |= (mmr->data[mmr->data_index + 4] << mmr->bit_index);
+      mmr->data_index++;
+    }
+}
+
+typedef struct {
+  int val;
+  int n_bits;
+} cfd_node;
+
+const cfd_node cf_white_decode[] = {
+	{ 256, 12 },
+	{ 272, 12 },
+	{ 29, 8 },
+	{ 30, 8 },
+	{ 45, 8 },
+	{ 46, 8 },
+	{ 22, 7 },
+	{ 22, 7 },
+	{ 23, 7 },
+	{ 23, 7 },
+	{ 47, 8 },
+	{ 48, 8 },
+	{ 13, 6 },
+	{ 13, 6 },
+	{ 13, 6 },
+	{ 13, 6 },
+	{ 20, 7 },
+	{ 20, 7 },
+	{ 33, 8 },
+	{ 34, 8 },
+	{ 35, 8 },
+	{ 36, 8 },
+	{ 37, 8 },
+	{ 38, 8 },
+	{ 19, 7 },
+	{ 19, 7 },
+	{ 31, 8 },
+	{ 32, 8 },
+	{ 1, 6 },
+	{ 1, 6 },
+	{ 1, 6 },
+	{ 1, 6 },
+	{ 12, 6 },
+	{ 12, 6 },
+	{ 12, 6 },
+	{ 12, 6 },
+	{ 53, 8 },
+	{ 54, 8 },
+	{ 26, 7 },
+	{ 26, 7 },
+	{ 39, 8 },
+	{ 40, 8 },
+	{ 41, 8 },
+	{ 42, 8 },
+	{ 43, 8 },
+	{ 44, 8 },
+	{ 21, 7 },
+	{ 21, 7 },
+	{ 28, 7 },
+	{ 28, 7 },
+	{ 61, 8 },
+	{ 62, 8 },
+	{ 63, 8 },
+	{ 0, 8 },
+	{ 320, 8 },
+	{ 384, 8 },
+	{ 10, 5 },
+	{ 10, 5 },
+	{ 10, 5 },
+	{ 10, 5 },
+	{ 10, 5 },
+	{ 10, 5 },
+	{ 10, 5 },
+	{ 10, 5 },
+	{ 11, 5 },
+	{ 11, 5 },
+	{ 11, 5 },
+	{ 11, 5 },
+	{ 11, 5 },
+	{ 11, 5 },
+	{ 11, 5 },
+	{ 11, 5 },
+	{ 27, 7 },
+	{ 27, 7 },
+	{ 59, 8 },
+	{ 60, 8 },
+	{ 288, 9 },
+	{ 290, 9 },
+	{ 18, 7 },
+	{ 18, 7 },
+	{ 24, 7 },
+	{ 24, 7 },
+	{ 49, 8 },
+	{ 50, 8 },
+	{ 51, 8 },
+	{ 52, 8 },
+	{ 25, 7 },
+	{ 25, 7 },
+	{ 55, 8 },
+	{ 56, 8 },
+	{ 57, 8 },
+	{ 58, 8 },
+	{ 192, 6 },
+	{ 192, 6 },
+	{ 192, 6 },
+	{ 192, 6 },
+	{ 1664, 6 },
+	{ 1664, 6 },
+	{ 1664, 6 },
+	{ 1664, 6 },
+	{ 448, 8 },
+	{ 512, 8 },
+	{ 292, 9 },
+	{ 640, 8 },
+	{ 576, 8 },
+	{ 294, 9 },
+	{ 296, 9 },
+	{ 298, 9 },
+	{ 300, 9 },
+	{ 302, 9 },
+	{ 256, 7 },
+	{ 256, 7 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 2, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 3, 4 },
+	{ 128, 5 },
+	{ 128, 5 },
+	{ 128, 5 },
+	{ 128, 5 },
+	{ 128, 5 },
+	{ 128, 5 },
+	{ 128, 5 },
+	{ 128, 5 },
+	{ 8, 5 },
+	{ 8, 5 },
+	{ 8, 5 },
+	{ 8, 5 },
+	{ 8, 5 },
+	{ 8, 5 },
+	{ 8, 5 },
+	{ 8, 5 },
+	{ 9, 5 },
+	{ 9, 5 },
+	{ 9, 5 },
+	{ 9, 5 },
+	{ 9, 5 },
+	{ 9, 5 },
+	{ 9, 5 },
+	{ 9, 5 },
+	{ 16, 6 },
+	{ 16, 6 },
+	{ 16, 6 },
+	{ 16, 6 },
+	{ 17, 6 },
+	{ 17, 6 },
+	{ 17, 6 },
+	{ 17, 6 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 4, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 14, 6 },
+	{ 14, 6 },
+	{ 14, 6 },
+	{ 14, 6 },
+	{ 15, 6 },
+	{ 15, 6 },
+	{ 15, 6 },
+	{ 15, 6 },
+	{ 64, 5 },
+	{ 64, 5 },
+	{ 64, 5 },
+	{ 64, 5 },
+	{ 64, 5 },
+	{ 64, 5 },
+	{ 64, 5 },
+	{ 64, 5 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ 7, 4 },
+	{ -2, 3 },
+	{ -2, 3 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -3, 4 },
+	{ 1792, 3 },
+	{ 1792, 3 },
+	{ 1984, 4 },
+	{ 2048, 4 },
+	{ 2112, 4 },
+	{ 2176, 4 },
+	{ 2240, 4 },
+	{ 2304, 4 },
+	{ 1856, 3 },
+	{ 1856, 3 },
+	{ 1920, 3 },
+	{ 1920, 3 },
+	{ 2368, 4 },
+	{ 2432, 4 },
+	{ 2496, 4 },
+	{ 2560, 4 },
+	{ 1472, 1 },
+	{ 1536, 1 },
+	{ 1600, 1 },
+	{ 1728, 1 },
+	{ 704, 1 },
+	{ 768, 1 },
+	{ 832, 1 },
+	{ 896, 1 },
+	{ 960, 1 },
+	{ 1024, 1 },
+	{ 1088, 1 },
+	{ 1152, 1 },
+	{ 1216, 1 },
+	{ 1280, 1 },
+	{ 1344, 1 },
+	{ 1408, 1 }
+};
+
+/* Black decoding table. */
+const cfd_node cf_black_decode[] = {
+	{ 128, 12 },
+	{ 160, 13 },
+	{ 224, 12 },
+	{ 256, 12 },
+	{ 10, 7 },
+	{ 11, 7 },
+	{ 288, 12 },
+	{ 12, 7 },
+	{ 9, 6 },
+	{ 9, 6 },
+	{ 8, 6 },
+	{ 8, 6 },
+	{ 7, 5 },
+	{ 7, 5 },
+	{ 7, 5 },
+	{ 7, 5 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 6, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 5, 4 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 1, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 4, 3 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 3, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ 2, 2 },
+	{ -2, 4 },
+	{ -2, 4 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -1, 0 },
+	{ -3, 5 },
+	{ 1792, 4 },
+	{ 1792, 4 },
+	{ 1984, 5 },
+	{ 2048, 5 },
+	{ 2112, 5 },
+	{ 2176, 5 },
+	{ 2240, 5 },
+	{ 2304, 5 },
+	{ 1856, 4 },
+	{ 1856, 4 },
+	{ 1920, 4 },
+	{ 1920, 4 },
+	{ 2368, 5 },
+	{ 2432, 5 },
+	{ 2496, 5 },
+	{ 2560, 5 },
+	{ 18, 3 },
+	{ 18, 3 },
+	{ 18, 3 },
+	{ 18, 3 },
+	{ 18, 3 },
+	{ 18, 3 },
+	{ 18, 3 },
+	{ 18, 3 },
+	{ 52, 5 },
+	{ 52, 5 },
+	{ 640, 6 },
+	{ 704, 6 },
+	{ 768, 6 },
+	{ 832, 6 },
+	{ 55, 5 },
+	{ 55, 5 },
+	{ 56, 5 },
+	{ 56, 5 },
+	{ 1280, 6 },
+	{ 1344, 6 },
+	{ 1408, 6 },
+	{ 1472, 6 },
+	{ 59, 5 },
+	{ 59, 5 },
+	{ 60, 5 },
+	{ 60, 5 },
+	{ 1536, 6 },
+	{ 1600, 6 },
+	{ 24, 4 },
+	{ 24, 4 },
+	{ 24, 4 },
+	{ 24, 4 },
+	{ 25, 4 },
+	{ 25, 4 },
+	{ 25, 4 },
+	{ 25, 4 },
+	{ 1664, 6 },
+	{ 1728, 6 },
+	{ 320, 5 },
+	{ 320, 5 },
+	{ 384, 5 },
+	{ 384, 5 },
+	{ 448, 5 },
+	{ 448, 5 },
+	{ 512, 6 },
+	{ 576, 6 },
+	{ 53, 5 },
+	{ 53, 5 },
+	{ 54, 5 },
+	{ 54, 5 },
+	{ 896, 6 },
+	{ 960, 6 },
+	{ 1024, 6 },
+	{ 1088, 6 },
+	{ 1152, 6 },
+	{ 1216, 6 },
+	{ 64, 3 },
+	{ 64, 3 },
+	{ 64, 3 },
+	{ 64, 3 },
+	{ 64, 3 },
+	{ 64, 3 },
+	{ 64, 3 },
+	{ 64, 3 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 13, 1 },
+	{ 23, 4 },
+	{ 23, 4 },
+	{ 50, 5 },
+	{ 51, 5 },
+	{ 44, 5 },
+	{ 45, 5 },
+	{ 46, 5 },
+	{ 47, 5 },
+	{ 57, 5 },
+	{ 58, 5 },
+	{ 61, 5 },
+	{ 256, 5 },
+	{ 16, 3 },
+	{ 16, 3 },
+	{ 16, 3 },
+	{ 16, 3 },
+	{ 17, 3 },
+	{ 17, 3 },
+	{ 17, 3 },
+	{ 17, 3 },
+	{ 48, 5 },
+	{ 49, 5 },
+	{ 62, 5 },
+	{ 63, 5 },
+	{ 30, 5 },
+	{ 31, 5 },
+	{ 32, 5 },
+	{ 33, 5 },
+	{ 40, 5 },
+	{ 41, 5 },
+	{ 22, 4 },
+	{ 22, 4 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 14, 1 },
+	{ 15, 2 },
+	{ 15, 2 },
+	{ 15, 2 },
+	{ 15, 2 },
+	{ 15, 2 },
+	{ 15, 2 },
+	{ 15, 2 },
+	{ 15, 2 },
+	{ 128, 5 },
+	{ 192, 5 },
+	{ 26, 5 },
+	{ 27, 5 },
+	{ 28, 5 },
+	{ 29, 5 },
+	{ 19, 4 },
+	{ 19, 4 },
+	{ 20, 4 },
+	{ 20, 4 },
+	{ 34, 5 },
+	{ 35, 5 },
+	{ 36, 5 },
+	{ 37, 5 },
+	{ 38, 5 },
+	{ 39, 5 },
+	{ 21, 4 },
+	{ 21, 4 },
+	{ 42, 5 },
+	{ 43, 5 },
+	{ 0, 3 },
+	{ 0, 3 },
+	{ 0, 3 },
+	{ 0, 3 }
+};
+
+static int
+jbig2_decode_get_code(Jbig2MmrCtx *mmr, const cfd_node *table, int initial_bits)
+{
+  uint32_t word = mmr->word;
+  int table_ix = word >> (32 - initial_bits);
+  int val = table[table_ix].val;
+  int n_bits = table[table_ix].n_bits;
+
+  if (n_bits > 8)
+    {
+      int mask = (1 << (32 - initial_bits)) - 1;
+      table_ix = val + ((word & mask) >> (32 - n_bits));
+      val = table[table_ix].val;
+      n_bits = 8 + table[table_ix].n_bits + 8;
+    }
+  jbig2_decode_mmr_consume(mmr, n_bits);
+  return val;
+}
+
+static int
+jbig2_decode_get_run(Jbig2MmrCtx *mmr, const cfd_node *table, int initial_bits)
+{
+  int result = 0;
+  int val;
+
+  do
+    {
+      val = jbig2_decode_get_code(mmr, table, initial_bits);
+      result += val;
+    }
+  while (val >= 64);
+  return result;
+}
+
+static void
+jbig2_decode_mmr_line(Jbig2MmrCtx *mmr, const byte *ref, byte *dst)
+{
+  int a0 = 0;
+
+  while (1)
+    {
+      uint32_t word = mmr->word;
+      printf ("%08x\n", word);
+      if ((word >> (32 - 3)) == 1)
+	{
+	  int white_run, black_run;
+
+	  jbig2_decode_mmr_consume(mmr, 3);
+	  white_run = jbig2_decode_get_run(mmr, cf_white_decode, 8);
+	  black_run = jbig2_decode_get_run(mmr, cf_black_decode, 7);
+	  printf ("H %d %d\n", white_run, black_run);
+	}
+      else if ((word >> (32 - 4)) == 1)
+	{
+	  printf ("P\n");
+	  jbig2_decode_mmr_consume(mmr, 3);
+	}
+      else if ((word >> (32 - 1)) == 1)
+	{
+	  printf ("V(0)\n");
+	  jbig2_decode_mmr_consume(mmr, 1);
+	}
+      else if ((word >> (32 - 3)) == 3)
+	{
+	  printf ("VR(1)\n");
+	  jbig2_decode_mmr_consume(mmr, 3);
+	}
+      else if ((word >> (32 - 6)) == 3)
+	{
+	  printf ("VR(2)\n");
+	  jbig2_decode_mmr_consume(mmr, 6);
+	}
+      else if ((word >> (32 - 7)) == 3)
+	{
+	  printf ("VR(3)\n");
+	  jbig2_decode_mmr_consume(mmr, 7);
+	}
+      else if ((word >> (32 - 3)) == 2)
+	{
+	  printf ("VL(1)\n");
+	  jbig2_decode_mmr_consume(mmr, 3);
+	}
+      else if ((word >> (32 - 6)) == 2)
+	{
+	  printf ("VL(2)\n");
+	  jbig2_decode_mmr_consume(mmr, 6);
+	}
+      else if ((word >> (32 - 7)) == 2)
+	{
+	  printf ("VL(3)\n");
+	  jbig2_decode_mmr_consume(mmr, 7);
+	}
+      else
+	break;
+    }
+}
+
+int
+jbig2_decode_generic_mmr(Jbig2Ctx *ctx,
+			 int32_t seg_number,
+			 const Jbig2GenericRegionParams *params,
+			 const byte *data, size_t size,
+			 byte *gbreg)
+{
+  Jbig2MmrCtx mmr;
+  int rowstride = (params->GBW + 7) >> 3;
+  const byte *gbreg_line = gbreg;
+  int y;
+
+  jbig2_decode_mmr_init(&mmr, params->GBW, params->GBH, data, size);
+
+  for (y = 0; y < params->GBH; y++)
+    {
+      jbig2_decode_mmr_line(&mmr, gbreg_line);
+      gbreg_line += rowstride;
+    }
+}
--- /dev/null
+++ b/jbig2_mmr.h
@@ -1,0 +1,7 @@
+int
+jbig2_decode_generic_mmr(Jbig2Ctx *ctx,
+			 int32_t seg_number,
+			 const Jbig2GenericRegionParams *params,
+			 const byte *data, size_t size,
+			 byte *gbreg);
+
--- a/jbig2_symbol_dict.c
+++ b/jbig2_symbol_dict.c
@@ -135,6 +135,7 @@
 
 	  /* 6.5.5 (4c.iv) */
 	  NSYMSDECODED = NSYMSDECODED + 1;
+	  printf ("%d of %d decoded\n", NSYMSDECODED, params->SDNUMNEWSYMS);
 	}
     }
 
@@ -164,14 +165,17 @@
   params.SDTEMPLATE = (flags >> 10) & 3;
   params.SDRTEMPLATE = (flags >> 12) & 1;
 
+  if (params.SDHUFF)
+    return 0;
+
   /* FIXME: there are quite a few of these conditions to check */
   /* maybe #ifdef CONFORMANCE and a separate routine */
-  if(!params.SDHUFF && (flags & 0x0006))
+  if(!params.SDHUFF && (flags & 0x000c))
     {
       jbig2_error(ctx, JBIG2_SEVERITY_WARNING, sh->segment_number,
 		  "SDHUFF is zero, but contrary to spec SDHUFFDH is not.");
     }
-  if(!params.SDHUFF && (flags & 0x0018))
+  if(!params.SDHUFF && (flags & 0x0030))
     {
       jbig2_error(ctx, JBIG2_SEVERITY_WARNING, sh->segment_number,
 		  "SDHUFF is zero, but contrary to spec SDHUFFDW is not.");