ref: b1bfe4ae823054ac2dc94af15246fa489304cf80
parent: a42adfaa785b05670af63f8767c3ff55636c6686
author: Henry Stiles <[email protected]>
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'");