shithub: jbig2

Download patch

ref: b1bfe4ae823054ac2dc94af15246fa489304cf80
parent: a42adfaa785b05670af63f8767c3ff55636c6686
author: Henry Stiles <henry.stiles@artifex.com>
date: Fri Jan 13 05:40:50 EST 2012

Bug 691230, basic jbig2 halftone image support.

Thanks to George Gottleuber for this work.

--- a/jbig2_halftone.c
+++ b/jbig2_halftone.c
@@ -27,39 +27,9 @@
 #include "jbig2_arith.h"
 #include "jbig2_generic.h"
 #include "jbig2_mmr.h"
+#include "jbig2_image.h"
+#include "jbig2_halftone.h"
 
-typedef struct {
-  int n_patterns;
-  Jbig2Image **patterns;
-  int HPW, HPH;
-} Jbig2PatternDict;
-
-/* Table 24 */
-typedef struct {
-  bool HDMMR;
-  uint32_t HDPW;
-  uint32_t HDPH;
-  uint32_t GRAYMAX;
-  int HDTEMPLATE;
-} Jbig2PatternDictParams;
-
-/* Table 33 */
-typedef struct {
-  byte flags;
-  uint32_t HGW;
-  uint32_t HGH;
-  int32_t  HGX;
-  int32_t  HGY;
-  uint16_t HRX;
-  uint16_t HRY;
-  bool HMMR;
-  int HTEMPLATE;
-  bool HENABLESKIP;
-  Jbig2ComposeOp op;
-  bool HDEFPIXEL;
-} Jbig2HalftoneRegionParams;
-
-
 /**
  * jbig2_hd_new: create a new dictionary from a collective bitmap
  */
@@ -261,7 +231,7 @@
     {
       jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
           "failed to allocate GB_stats in pattern dictionary");
-      return NULL;
+      return 0;
     }
     memset(GB_stats, 0, stats_size);
   }
@@ -278,10 +248,213 @@
   return (segment->result != NULL) ? 0 : 1;
 }
 
+/**
+ * jbig2_decode_gray_scale_image: decode gray-scale image
+ *
+ * @ctx: jbig2 decoder context
+ * @segment: jbig2 segment (header) structure
+ * @data: pointer to text region data to be decoded
+ * @size: length of text region data
+ * @GSMMR: if MMR is used
+ * @GSW: width of gray-scale image
+ * @GSH: height of gray-scale image
+ * @GSBPP: number of bitplanes/Jbig2Images to use 
+ * @GSKIP: mask indicating which values should be skipped
+ * @GSTEMPLATE: template used to code the gray-scale bitplanes
+ * @GB_stats: artimetic coding context to use
+ *
+ * Implements the decoding a gray-scale image described in
+ * annex C.5. This is part of the halftone region decoding.
+ *
+ * returns: array of gray-scale values with GSW x GSH width/height
+ *          0 on failure
+ **/
+uint8_t **
+jbig2_decode_gray_scale_image(Jbig2Ctx *ctx, Jbig2Segment* segment,
+                              const byte *data, const size_t size,
+                              bool GSMMR, uint32_t GSW, uint32_t GSH, 
+                              uint32_t GSBPP, bool GSUSESKIP,
+                              Jbig2Image *GSKIP, int GSTEMPLATE,
+                              Jbig2ArithCx *GB_stats)
+{
+  uint8_t **GSVALS;
+  size_t consumed_bytes = 0;
+  int i, j, code, stride;
+  int x, y;
+  Jbig2Image **GSPLANES;
+  Jbig2GenericRegionParams rparams;
+  Jbig2WordStream *ws = NULL;
+  Jbig2ArithState *as = NULL;
 
+  /* allocate GSPLANES */
+  GSPLANES = jbig2_new(ctx, Jbig2Image*, GSBPP);
+  if (GSPLANES == NULL) {
+    jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, 
+                "failed to allocate %d bytes for GSPLANES", GSBPP);
+    return NULL;
+  }
 
+  for (i = 0; i < GSBPP; ++i) {
+    GSPLANES[i] = jbig2_image_new(ctx, GSW, GSH);
+    if (GSPLANES[i] == NULL) {
+      jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1,
+                  "failed to allocate %dx%d image for GSPLANES", GSW, GSH);
+      /* free already allocated */
+      for (j = i-1; j >= 0; --j) {
+        jbig2_image_release(ctx, GSPLANES[j]);
+      }
+      jbig2_free(ctx->allocator, GSPLANES);
+      return NULL;
+    }
+  }
+
+  /* C.5 step 1. Decode GSPLANES[GSBPP-1] */ 
+  /* fill generic region decoder parameters */
+  rparams.MMR = GSMMR;
+  rparams.GBTEMPLATE = GSTEMPLATE;
+  rparams.TPGDON = 0;
+  rparams.USESKIP = GSUSESKIP;
+  rparams.gbat[0] = (GSTEMPLATE <= 1? 3 : 2);
+  rparams.gbat[1] = -1;
+  rparams.gbat[2] = -3;
+  rparams.gbat[3] = -1;
+  rparams.gbat[4] = 2;
+  rparams.gbat[5] = -2;
+  rparams.gbat[6] = -2;
+  rparams.gbat[7] = -2;
+
+  if (GSMMR) {
+    code = jbig2_decode_halftone_mmr(ctx, &rparams, data, size,
+                                     GSPLANES[GSBPP-1], &consumed_bytes);
+  } else {
+    ws = jbig2_word_stream_buf_new(ctx, data, size);
+    as = jbig2_arith_new(ctx, ws);
+
+    code = jbig2_decode_generic_region(ctx, segment, &rparams, as,
+                                       GSPLANES[GSBPP-1], GB_stats);
+
+  }
+  if (code != 0) {
+    jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
+                "error decoding GSPLANES for halftone image");
+  }
+
+  /* C.5 step 2. Set j = GSBPP-2 */ 
+  j = GSBPP - 2;
+  /* C.5 step 3. decode loop */ 
+  while(j >= 0) {
+    /*  C.5 step 3. (a) */
+    if (GSMMR) {
+      code = jbig2_decode_halftone_mmr(ctx, &rparams, data + consumed_bytes,
+                                       size - consumed_bytes, GSPLANES[j],
+                                       &consumed_bytes);
+    } else {
+      code = jbig2_decode_generic_region(ctx, segment, &rparams, as,
+                                         GSPLANES[j], GB_stats);
+    }
+    if (code != 0) {
+      jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
+                  "error decoding GSPLANES for halftone image");
+    }
+
+    /* C.5 step 3. (b):
+     * for each [x,y]
+     * GSPLANES[j][x][y] = GSPLANES[j+1][x][y] XOR GSPLANES[j][x][y] */
+    stride = GSPLANES[0]->stride;
+    for (i=0; i < stride * GSH; ++i)
+      GSPLANES[j]->data[i] ^= GSPLANES[j+1]->data[i];
+
+    /*  C.5 step 3. (c) */
+    --j;
+  }
+
+  /* allocate GSVALS */
+  GSVALS = jbig2_new(ctx, uint8_t* , GSW);
+  if (GSVALS == NULL) {
+    jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1,
+                "failed to allocate GSVALS: %d bytes", GSW);
+    return NULL;
+  }
+  for (i=0; i<GSW; ++i) {
+    GSVALS[i] = jbig2_new(ctx, uint8_t , GSH);
+    if (GSVALS[i] == NULL) {
+      jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1,
+                  "failed to allocate GSVALS: %d bytes", GSH * GSW);
+      return NULL;
+    }
+  }
+
+  /*  C.5 step 4.  */
+  for (x = 0; x < GSW; ++x) {
+    for (y = 0; y < GSH; ++y) {
+      GSVALS[x][y] = 0;
+
+      for (j = 0; j < GSBPP; ++j)
+        GSVALS[x][y] += jbig2_image_get_pixel(GSPLANES[j], x, y) << j;
+    }
+  }
+
+  /* free memory */
+  if (!GSMMR) {
+    jbig2_free(ctx->allocator, as);
+    jbig2_word_stream_buf_free(ctx, ws);
+  }
+  for (i=0; i< GSBPP; ++i)
+    jbig2_image_release(ctx, GSPLANES[i]);
+
+  jbig2_free(ctx->allocator, GSPLANES);
+
+  return GSVALS;
+}
+
 /**
+ * jbig2_decode_ht_region_get_hpats: get pattern dictionary 
+ *
+ * @ctx: jbig2 decoder context
+ * @segment: jbig2 halftone region segment 
+ *
+ * Returns the first referred pattern dictionary of segment 
+ *
+ * returns: pattern dictionary
+ *          0 if search failed
+ **/
+Jbig2PatternDict * 
+jbig2_decode_ht_region_get_hpats(Jbig2Ctx *ctx, Jbig2Segment *segment)
+{
+  int index = 0;
+  Jbig2PatternDict *pattern_dict = NULL;
+  Jbig2Segment *rsegment = NULL;
+
+  /* loop through all referred segments */
+  while (!pattern_dict && segment->referred_to_segment_count > index) {
+    rsegment = jbig2_find_segment(ctx, segment->referred_to_segments[index]);
+    if (rsegment) {
+      /* segment type is pattern dictionary and result is not empty */
+      if ((rsegment->flags & 0x3f) == 16 && rsegment->result) {
+        pattern_dict = (Jbig2PatternDict *) rsegment->result;
+        return pattern_dict;
+      }
+    }
+    index++;
+  }
+  return pattern_dict;
+}
+
+/**
  * jbig2_decode_halftone_region: decode a halftone region
+ *
+ * @ctx: jbig2 decoder context
+ * @segment: jbig2 halftone region segment 
+ * @params: parameters 
+ * @data: pointer to halftone region data to be decoded
+ * @size: length of halftone region data
+ * @GB_stats: artimetic coding context to use
+ *
+ * Implements the halftone region decoding proceedure
+ * described in section 6.6.5 of the JBIG2 spec.
+ *
+ * returns: 0 on success
+ *         <0 on failure
  **/
 int
 jbig2_decode_halftone_region(Jbig2Ctx *ctx, Jbig2Segment *segment,
@@ -290,12 +463,80 @@
 			     Jbig2Image *image,
 			     Jbig2ArithCx *GB_stats)
 {
-  int code = 0;
+  uint32_t HBPP;
+  uint32_t HNUMPATS;
+  uint8_t **GI;
+  Jbig2Image *HSKIP = NULL;
+  Jbig2PatternDict * HPATS;
+  int i;
+  uint32_t mg, ng;
+  int32_t x, y;
+  uint8_t gray_val;
 
-  code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
-      "decode a halftone region not yet implemented");
+  /* 6.6.5 point 1. Fill bitmap with HDEFPIXEL */
+  memset(image->data, params->HDEFPIXEL, image->stride * image->height);
 
-  return code;
+  /* 6.6.5 point 2. compute HSKIP */
+  if (params->HENABLESKIP == 1) {
+    jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
+                "unhandled option HENABLESKIP");
+  }
+
+  /* 6.6.5 point 3. set HBPP to ceil(log2(HNUMPATS)): 
+   * we need the number of patterns used in this region (HNUMPATS)
+   * get it from referred pattern dictionary */
+
+  HPATS = jbig2_decode_ht_region_get_hpats(ctx, segment);
+  if (!HPATS) {
+    jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
+    "no pattern dictionary found, skipping halftone image");
+    return -1;
+  }
+  HNUMPATS = HPATS->n_patterns;
+
+  /* calculate ceil(log2(HNUMPATS)) */
+  HBPP = 0; 
+  while(HNUMPATS > (1 << ++HBPP));
+
+  /* 6.6.5 point 4. decode gray-scale image as mentioned in annex C */
+  GI = jbig2_decode_gray_scale_image(ctx, segment, data, size,
+                                     params->HMMR, params->HGW,
+                                     params->HGH, HBPP, params->HENABLESKIP,
+                                     HSKIP, params->HTEMPLATE,
+                                     GB_stats); 
+
+  if (!GI) {
+    jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
+    "unable to acquire gray-scale image, skipping halftone image");
+    return -1;
+  }
+
+  /* 6.6.5 point 5. place patterns with procedure mentioned in 6.6.5.2 */
+  for (mg = 0 ; mg < params->HGH ; ++mg) {
+    for (ng = 0 ; ng < params->HGW ; ++ng ) {
+      x = (params->HGX + mg * params->HRY + ng * params->HRX) >> 8;
+      y = (params->HGY + mg * params->HRX - ng * params->HRY) >> 8;
+
+      /* prevent pattern index >= HNUMPATS */
+      gray_val = GI[ng][mg];
+      if (gray_val >= HNUMPATS) {
+        jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
+                    "gray-scale image uses value %d which larger than pattern dictionary",
+                    gray_val);
+        /* use highest aviable pattern */
+        gray_val = HNUMPATS - 1;
+      }
+      jbig2_image_compose(ctx, image, HPATS->patterns[gray_val], x, y, params->op);
+    }
+  }
+
+  /* free GI */
+  for (i = 0; i < params->HGW; ++i) {
+    jbig2_free(ctx->allocator, GI[i]);
+  }
+  jbig2_free(ctx->allocator, GI);
+
+  return 0;
 }
 
 /**
@@ -394,6 +635,10 @@
   if (!params.HMMR) {
     jbig2_free(ctx->allocator, GB_stats);
   }
+
+  jbig2_page_add_result(ctx, &ctx->pages[ctx->current_page],
+			image, region_info.x, region_info.y, region_info.op);
+  jbig2_image_release(ctx, image);
 
   return code;
 
--- /dev/null
+++ b/jbig2_halftone.h
@@ -1,0 +1,76 @@
+/*
+    jbig2dec
+
+    Copyright (C) 2012 Artifex Software, Inc.
+
+    This software is distributed under license and may not
+    be copied, modified or distributed except as expressly
+    authorized under the terms of the license contained in
+    the file LICENSE in this distribution.
+
+    For further licensing information refer to http://artifex.com/ or
+    contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+    San Rafael, CA  94903, U.S.A., +1(415)492-9861.
+*/
+
+#ifndef _JBIG2_HALFTONE_H
+#define _JBIG2_HALFTONE_H
+
+typedef struct {
+  int n_patterns;
+  Jbig2Image **patterns;
+  int HPW, HPH;
+} Jbig2PatternDict;
+
+
+/* Table 24 */
+typedef struct {
+  bool HDMMR;
+  uint32_t HDPW;
+  uint32_t HDPH;
+  uint32_t GRAYMAX;
+  int HDTEMPLATE;
+} Jbig2PatternDictParams;
+
+/* Table 33 */
+typedef struct {
+  byte flags;
+  uint32_t HGW;
+  uint32_t HGH;
+  int32_t  HGX;
+  int32_t  HGY;
+  uint16_t HRX;
+  uint16_t HRY;
+  bool HMMR;
+  int HTEMPLATE;
+  bool HENABLESKIP;
+  Jbig2ComposeOp op;
+  bool HDEFPIXEL;
+} Jbig2HalftoneRegionParams;
+
+Jbig2PatternDict* 
+jbig2_hd_new(Jbig2Ctx *ctx, const Jbig2PatternDictParams *params, 
+             Jbig2Image *image);
+
+void
+jbig2_hd_release(Jbig2Ctx *ctx, Jbig2PatternDict *dict);
+
+uint8_t **
+jbig2_decode_gray_scale_image(Jbig2Ctx *ctx, Jbig2Segment* segment,
+                              const byte *data, const size_t size,
+                              bool GSMMR, uint32_t GSW, uint32_t GSH,
+                              uint32_t GSBPP, bool GSUSESKIP,
+                              Jbig2Image *GSKIP, int GSTEMPLATE,
+                              Jbig2ArithCx *GB_stats);
+
+Jbig2PatternDict *
+jbig2_decode_ht_region_get_hpats(Jbig2Ctx *ctx, Jbig2Segment *segment);
+
+int
+jbig2_decode_halftone_region(Jbig2Ctx *ctx, Jbig2Segment *segment,
+                             Jbig2HalftoneRegionParams *params,
+                             const byte *data, const size_t size,
+                             Jbig2Image *image,
+                             Jbig2ArithCx *GB_stats);
+
+#endif /* _JBIG2_HALFTONE_H */
--- a/jbig2_mmr.c
+++ b/jbig2_mmr.c
@@ -996,3 +996,48 @@
 	return 0;
 }
 
+/**
+ * jbig2_decode_halftone_mmr: decode mmr region inside of halftones
+ * 
+ * @ctx: jbig2 decoder context
+ * @params: parameters for decoding  
+ * @data: pointer to text region data to be decoded
+ * @size: length of text region data
+ * @image: return of decoded image 
+ * @consumed_bytes: return of consumed bytes from @data
+ *
+ * MMR decoding that consumes EOFB and returns consumed bytes (@consumed_bytes)
+ *
+ * returns: 0 
+ **/
+int
+jbig2_decode_halftone_mmr(Jbig2Ctx *ctx,
+	const Jbig2GenericRegionParams *params,
+	const byte *data, size_t size,
+	Jbig2Image *image, size_t* consumed_bytes)
+{
+	Jbig2MmrCtx mmr;
+	const int rowstride = image->stride;
+	byte *dst = image->data;
+	byte *ref = NULL;
+	int y;
+	const uint32_t EOFB = 0x001001;
+
+	jbig2_decode_mmr_init(&mmr, image->width, image->height, data, size);
+
+	for (y = 0; y < image->height; y++) {
+		memset(dst, 0, rowstride);
+		jbig2_decode_mmr_line(&mmr, ref, dst);
+		ref = dst;
+		dst += rowstride;
+	}
+
+	/* test for EOFB (see section 6.2.6) */
+	if (mmr.word >> 8 == EOFB) {
+		mmr.data_index += 3;
+	}
+
+	*consumed_bytes += mmr.data_index + (mmr.bit_index >> 3) + 
+                       (mmr.bit_index > 0 ? 1 : 0);
+	return 0;
+}
--- a/jbig2_mmr.h
+++ b/jbig2_mmr.h
@@ -20,3 +20,9 @@
 			 const byte *data, size_t size,
 			 Jbig2Image *image);
 
+int
+jbig2_decode_halftone_mmr(Jbig2Ctx *ctx,
+	const Jbig2GenericRegionParams *params,
+	const byte *data, size_t size,
+	Jbig2Image *image, size_t* consumed_bytes);
+
--- a/jbig2_segment.c
+++ b/jbig2_segment.c
@@ -253,7 +253,6 @@
     case 6: /* immediate text region */
     case 7: /* immediate lossless text region */
       return jbig2_text_region(ctx, segment, segment_data);
-#ifdef JBIG2_HALFTONE
     case 16:
       return jbig2_pattern_dictionary(ctx, segment, segment_data);
     case 20: /* intermediate halftone region */
@@ -260,20 +259,6 @@
     case 22: /* immediate halftone region */
     case 23: /* immediate lossless halftone region */
       return jbig2_halftone_region(ctx, segment, segment_data);
-#else
-    case 16:
-      return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
-        "unhandled segment type 'pattern dictionary'");
-    case 20:
-      return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
-        "unhandled segment type 'intermediate halftone region'");
-    case 22:
-      return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
-        "unhandled segment type 'immediate halftone region'");
-    case 23:
-      return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
-        "unhandled segment type 'immediate lossless halftone region'");
-#endif
     case 36:
       return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
         "unhandled segment type 'intermediate generic region'");