373 lines
10 KiB
C
373 lines
10 KiB
C
|
/*
|
||
|
jbig2dec
|
||
|
|
||
|
Copyright (C) 2005 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.
|
||
|
*/
|
||
|
|
||
|
/* JBIG2 Pattern Dictionary and Halftone Region decoding */
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
#include "os_types.h"
|
||
|
|
||
|
#include <string.h> /* memset() */
|
||
|
|
||
|
#include "jbig2.h"
|
||
|
#include "jbig2_priv.h"
|
||
|
#include "jbig2_arith.h"
|
||
|
#include "jbig2_generic.h"
|
||
|
#include "jbig2_mmr.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
|
||
|
*/
|
||
|
Jbig2PatternDict *
|
||
|
jbig2_hd_new(Jbig2Ctx *ctx,
|
||
|
const Jbig2PatternDictParams *params,
|
||
|
Jbig2Image *image)
|
||
|
{
|
||
|
Jbig2PatternDict *new;
|
||
|
const int N = params->GRAYMAX + 1;
|
||
|
const int HPW = params->HDPW;
|
||
|
const int HPH = params->HDPH;
|
||
|
int i;
|
||
|
|
||
|
/* allocate a new struct */
|
||
|
new = (Jbig2PatternDict *)jbig2_alloc(ctx->allocator,
|
||
|
sizeof(Jbig2PatternDict));
|
||
|
if (new != NULL) {
|
||
|
new->patterns = (Jbig2Image **)jbig2_alloc(ctx->allocator,
|
||
|
N*sizeof(Jbig2Image*));
|
||
|
if (new->patterns == NULL) {
|
||
|
jbig2_free(ctx->allocator, new);
|
||
|
return NULL;
|
||
|
}
|
||
|
new->n_patterns = N;
|
||
|
new->HPW = HPW;
|
||
|
new->HPH = HPH;
|
||
|
|
||
|
/* 6.7.5(4) - copy out the individual pattern images */
|
||
|
for (i = 0; i < N; i++) {
|
||
|
new->patterns[i] = jbig2_image_new(ctx, HPW, HPH);
|
||
|
if (new->patterns[i] == NULL) {
|
||
|
int j;
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1,
|
||
|
"failed to allocate pattern element image");
|
||
|
for (j = 0; j < i; j++)
|
||
|
jbig2_free(ctx->allocator, new->patterns[j]);
|
||
|
jbig2_free(ctx->allocator, new);
|
||
|
return NULL;
|
||
|
}
|
||
|
/* compose with the REPLACE operator; the source
|
||
|
will be clipped to the destintion, selecting the
|
||
|
proper sub image */
|
||
|
jbig2_image_compose(ctx, new->patterns[i], image,
|
||
|
-i * HPW, 0, JBIG2_COMPOSE_REPLACE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* jbig2_hd_release: release a pattern dictionary
|
||
|
*/
|
||
|
void
|
||
|
jbig2_hd_release(Jbig2Ctx *ctx, Jbig2PatternDict *dict)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (dict == NULL) return;
|
||
|
for (i = 0; i < dict->n_patterns; i++)
|
||
|
if (dict->patterns[i]) jbig2_image_release(ctx, dict->patterns[i]);
|
||
|
jbig2_free(ctx->allocator, dict->patterns);
|
||
|
jbig2_free(ctx->allocator, dict);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* jbig2_decode_pattern_dict: decode pattern dictionary data
|
||
|
*
|
||
|
* @ctx: jbig2 decoder context
|
||
|
* @segment: jbig2 segment (header) structure
|
||
|
* @params: parameters from the pattern dictionary header
|
||
|
* @data: pointer to text region data to be decoded
|
||
|
* @size: length of text region data
|
||
|
* @GB_stats: artimetic coding context to use
|
||
|
*
|
||
|
* Implements the patten dictionary decoding proceedure
|
||
|
* described in section 6.7 of the JBIG2 spec.
|
||
|
*
|
||
|
* returns: a pointer to the resulting dictionary on success
|
||
|
* returns: 0 on failure
|
||
|
**/
|
||
|
static Jbig2PatternDict *
|
||
|
jbig2_decode_pattern_dict(Jbig2Ctx *ctx, Jbig2Segment *segment,
|
||
|
const Jbig2PatternDictParams *params,
|
||
|
const byte *data, const size_t size,
|
||
|
Jbig2ArithCx *GB_stats)
|
||
|
{
|
||
|
Jbig2PatternDict *hd = NULL;
|
||
|
Jbig2Image *image;
|
||
|
Jbig2GenericRegionParams rparams;
|
||
|
int code;
|
||
|
|
||
|
/* allocate the collective image */
|
||
|
image = jbig2_image_new(ctx,
|
||
|
params->HDPW * (params->GRAYMAX + 1), params->HDPH);
|
||
|
if (image == NULL) {
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
|
||
|
"failed to allocate collective bitmap for halftone dict!");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* fill out the generic region decoder parameters */
|
||
|
rparams.MMR = params->HDMMR;
|
||
|
rparams.GBTEMPLATE = params->HDTEMPLATE;
|
||
|
rparams.TPGDON = 0; /* not used if HDMMR = 1 */
|
||
|
rparams.USESKIP = 0;
|
||
|
rparams.gbat[0] = -params->HDPW;
|
||
|
rparams.gbat[1] = 0;
|
||
|
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 (params->HDMMR) {
|
||
|
code = jbig2_decode_generic_mmr(ctx, segment, &rparams,
|
||
|
data, size, image);
|
||
|
} else {
|
||
|
Jbig2WordStream *ws = jbig2_word_stream_buf_new(ctx, data, size);
|
||
|
Jbig2ArithState *as = jbig2_arith_new(ctx, ws);
|
||
|
|
||
|
code = jbig2_decode_generic_region(ctx, segment, &rparams,
|
||
|
as, image, GB_stats);
|
||
|
|
||
|
jbig2_free(ctx->allocator, as);
|
||
|
jbig2_word_stream_buf_free(ctx, ws);
|
||
|
}
|
||
|
if (code != 0) {
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
|
||
|
"error decoding collective pattern dictionary bitmap!");
|
||
|
}
|
||
|
|
||
|
hd = jbig2_hd_new(ctx, params, image);
|
||
|
jbig2_image_release(ctx, image);
|
||
|
|
||
|
return hd;
|
||
|
}
|
||
|
|
||
|
/* 7.4.4 */
|
||
|
int
|
||
|
jbig2_pattern_dictionary(Jbig2Ctx *ctx, Jbig2Segment *segment,
|
||
|
const byte *segment_data)
|
||
|
{
|
||
|
Jbig2PatternDictParams params;
|
||
|
Jbig2ArithCx *GB_stats = NULL;
|
||
|
byte flags;
|
||
|
int offset = 0;
|
||
|
|
||
|
/* 7.4.4.1 - Data header */
|
||
|
if (segment->data_length < 7) {
|
||
|
return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
|
||
|
"Segment too short");
|
||
|
}
|
||
|
flags = segment_data[0];
|
||
|
params.HDMMR = flags & 1;
|
||
|
params.HDTEMPLATE = (flags & 6) >> 1;
|
||
|
params.HDPW = segment_data[1];
|
||
|
params.HDPH = segment_data[2];
|
||
|
params.GRAYMAX = jbig2_get_int32(segment_data + 3);
|
||
|
offset += 7;
|
||
|
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number,
|
||
|
"pattern dictionary, flags=%02x, %d grays (%dx%d cell)",
|
||
|
flags, params.GRAYMAX + 1, params.HDPW, params.HDPH);
|
||
|
|
||
|
if (params.HDMMR && params.HDTEMPLATE) {
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
|
||
|
"HDTEMPLATE is %d when HDMMR is %d, contrary to spec",
|
||
|
params.HDTEMPLATE, params.HDMMR);
|
||
|
}
|
||
|
if (flags & 0xf8) {
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
|
||
|
"Reserved flag bits non-zero");
|
||
|
}
|
||
|
|
||
|
/* 7.4.4.2 */
|
||
|
if (!params.HDMMR) {
|
||
|
/* allocate and zero arithmetic coding stats */
|
||
|
int stats_size = jbig2_generic_stats_size(ctx, params.HDTEMPLATE);
|
||
|
GB_stats = jbig2_alloc(ctx->allocator, stats_size);
|
||
|
memset(GB_stats, 0, stats_size);
|
||
|
}
|
||
|
|
||
|
segment->result = jbig2_decode_pattern_dict(ctx, segment, ¶ms,
|
||
|
segment_data + offset,
|
||
|
segment->data_length - offset, GB_stats);
|
||
|
|
||
|
/* todo: retain GB_stats? */
|
||
|
if (!params.HDMMR) {
|
||
|
jbig2_free(ctx->allocator, GB_stats);
|
||
|
}
|
||
|
|
||
|
return (segment->result != NULL) ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* jbig2_decode_halftone_region: decode a halftone region
|
||
|
**/
|
||
|
int
|
||
|
jbig2_decode_halftone_region(Jbig2Ctx *ctx, Jbig2Segment *segment,
|
||
|
Jbig2HalftoneRegionParams *params,
|
||
|
const byte *data, const size_t size,
|
||
|
Jbig2Image *image,
|
||
|
Jbig2ArithCx *GB_stats)
|
||
|
{
|
||
|
int code = 0;
|
||
|
|
||
|
/* todo: implement */
|
||
|
return code;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* jbig2_halftone_region: read a halftone region segment header
|
||
|
**/
|
||
|
int
|
||
|
jbig2_halftone_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data)
|
||
|
{
|
||
|
int offset = 0;
|
||
|
Jbig2RegionSegmentInfo region_info;
|
||
|
Jbig2HalftoneRegionParams params;
|
||
|
Jbig2Image *image;
|
||
|
Jbig2ArithCx *GB_stats;
|
||
|
int code;
|
||
|
|
||
|
/* 7.4.5.1 */
|
||
|
if (segment->data_length < 17) goto too_short;
|
||
|
jbig2_get_region_segment_info(®ion_info, segment_data);
|
||
|
offset += 17;
|
||
|
|
||
|
if (segment->data_length < 18) goto too_short;
|
||
|
|
||
|
/* 7.4.5.1.1 */
|
||
|
params.flags = segment_data[offset];
|
||
|
params.HMMR = params.flags & 1;
|
||
|
params.HTEMPLATE = (params.flags & 6) >> 1;
|
||
|
params.HENABLESKIP = (params.flags & 8) >> 3;
|
||
|
params.op = (params.flags & 0x70) >> 4;
|
||
|
params.HDEFPIXEL = (params.flags &0x80) >> 7;
|
||
|
offset += 1;
|
||
|
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number,
|
||
|
"halftone region: %d x %d @ (%x,%d) flags=%02x",
|
||
|
region_info.width, region_info.height,
|
||
|
region_info.x, region_info.y, params.flags);
|
||
|
|
||
|
if (params.HMMR && params.HTEMPLATE) {
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
|
||
|
"HTEMPLATE is %d when HMMR is %d, contrary to spec",
|
||
|
params.HTEMPLATE, params.HMMR);
|
||
|
}
|
||
|
if (params.HMMR && params.HENABLESKIP) {
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
|
||
|
"HENABLESKIP is %d when HMMR is %d, contrary to spec",
|
||
|
params.HENABLESKIP, params.HMMR);
|
||
|
}
|
||
|
|
||
|
/* Figure 43 */
|
||
|
if (segment->data_length - offset < 16) goto too_short;
|
||
|
params.HGW = jbig2_get_int32(segment_data + offset);
|
||
|
params.HGH = jbig2_get_int32(segment_data + offset + 4);
|
||
|
params.HGX = jbig2_get_int32(segment_data + offset + 8);
|
||
|
params.HGY = jbig2_get_int32(segment_data + offset + 12);
|
||
|
offset += 16;
|
||
|
|
||
|
/* Figure 44 */
|
||
|
if (segment->data_length - offset < 4) goto too_short;
|
||
|
params.HRX = jbig2_get_int16(segment_data + offset);
|
||
|
params.HRY = jbig2_get_int16(segment_data + offset + 2);
|
||
|
offset += 4;
|
||
|
|
||
|
jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number,
|
||
|
" grid %d x %d @ (%d.%d,%d.%d) vector (%d.%d,%d.%d)",
|
||
|
params.HGW, params.HGH,
|
||
|
params.HGX >> 8, params.HGX & 0xff,
|
||
|
params.HGY >> 8, params.HGY & 0xff,
|
||
|
params.HRX >> 8, params.HRX & 0xff,
|
||
|
params.HRY >> 8, params.HRY & 0xff);
|
||
|
|
||
|
/* 7.4.5.2.2 */
|
||
|
if (!params.HMMR) {
|
||
|
/* allocate and zero arithmetic coding stats */
|
||
|
int stats_size = jbig2_generic_stats_size(ctx, params.HTEMPLATE);
|
||
|
GB_stats = jbig2_alloc(ctx->allocator, stats_size);
|
||
|
memset(GB_stats, 0, stats_size);
|
||
|
}
|
||
|
|
||
|
image = jbig2_image_new(ctx, region_info.width, region_info.height);
|
||
|
if (image == NULL)
|
||
|
return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
|
||
|
"unable to allocate halftone image");
|
||
|
|
||
|
code = jbig2_decode_halftone_region(ctx, segment, ¶ms,
|
||
|
segment_data + offset, segment->data_length - offset,
|
||
|
image, GB_stats);
|
||
|
|
||
|
/* todo: retain GB_stats? */
|
||
|
if (!params.HMMR) {
|
||
|
jbig2_free(ctx->allocator, GB_stats);
|
||
|
}
|
||
|
|
||
|
return code;
|
||
|
|
||
|
too_short:
|
||
|
return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
|
||
|
"Segment too short");
|
||
|
}
|