ref: 107138382e590d3851ba1b5e897a33f2cf27ee4b
dir: /jbig2_generic.c/
/** * Generic region handlers. **/ #define OUTPUT_PBM #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" /* Table 2 */ typedef struct { bool MMR; int32_t GBW; int32_t GBH; int GBTEMPLATE; bool TPGDON; bool USESKIP; /* SKIP */ byte gbat[8]; } Jbig2GenericRegionParams; typedef struct { int32_t width; int32_t height; int32_t x; int32_t y; byte flags; } Jbig2RegionSegmentInfo; typedef struct { Jbig2WordStream super; const byte *data; size_t size; } Jbig2WordStreamBuf; static uint32_t jbig2_word_stream_buf_get_next_word(Jbig2WordStream *self, int offset) { Jbig2WordStreamBuf *z = (Jbig2WordStreamBuf *)self; const byte *data = z->data; uint32_t result; if (offset + 4 < z->size) result = (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]; else { int i; result = 0; for (i = 0; i < z->size - offset; i++) result |= data[offset + i] << ((3 - i) << 3); } return result; } Jbig2WordStream * jbig2_word_stream_buf_new(Jbig2Ctx *ctx, const byte *data, size_t size) { Jbig2WordStreamBuf *result = (Jbig2WordStreamBuf *)jbig2_alloc(ctx->allocator, sizeof(Jbig2WordStreamBuf)); result->super.get_next_word = jbig2_word_stream_buf_get_next_word; result->data = data; result->size = size; return &result->super; } void jbig2_word_stream_buf_free(Jbig2Ctx *ctx, Jbig2WordStream *ws) { jbig2_free(ctx->allocator, ws); } static int jbig2_decode_generic_template1(Jbig2Ctx *ctx, int32_t seg_number, const Jbig2GenericRegionParams *params, const byte *data, size_t size, byte *gbreg) { Jbig2ArithCx GB_stats[8192]; Jbig2WordStream *ws = jbig2_word_stream_buf_new(ctx, data, size); /* todo: ctx needs to be an argument; fix up memory allocation */ Jbig2ArithState *as = jbig2_arith_new(ws); int GBW = params->GBW; int rowstride = (GBW + 7) >> 3; int x, y; byte *gbreg_line = gbreg; bool LTP = 0; memset(GB_stats, 0, sizeof(GB_stats)); /* 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)] << 5 : 0; CONTEXT = ((line_m1 >> 1) & 0x1f8) | ((line_m2 >> 4) & 0x1e00); /* 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] << 5: 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 & 0xefb) << 1) | bit | ((line_m1 >> (8 - x_minor)) & 0x8) | ((line_m2 >> (8 - x_minor)) & 0x200); } 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. * @gbreg: Where to store the decoded data. * * 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. * * Return code: 0 on success. **/ int jbig2_decode_generic_region(Jbig2Ctx *ctx, int32_t seg_number, const Jbig2GenericRegionParams *params, const byte *data, size_t size, byte *gbreg) { if (!params->MMR && params->GBTEMPLATE == 1) return jbig2_decode_generic_template1(ctx, seg_number, params, data, size, gbreg); jbig2_error(ctx, JBIG2_SEVERITY_WARNING, seg_number, "decode_generic_region: MMR=%d, GBTEMPLATE=%d NYI", params->MMR, params->GBTEMPLATE); return -1; } void jbig2_get_region_segment_info(Jbig2RegionSegmentInfo *info, const byte *segment_data) { /* 7.4.1 */ info->width = jbig2_get_int32(segment_data); info->height = jbig2_get_int32(segment_data + 4); info->x = jbig2_get_int32(segment_data + 8); info->y = jbig2_get_int32(segment_data + 12); info->flags = segment_data[16]; } int jbig2_immediate_generic_region(Jbig2Ctx *ctx, Jbig2SegmentHeader *sh, const uint8_t *segment_data) { Jbig2RegionSegmentInfo rsi; byte seg_flags; int8_t gbat[8]; int offset; int gbat_bytes = 0; Jbig2GenericRegionParams params; int code; byte *gbreg; /* 7.4.6 */ if (sh->data_length < 18) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, sh->segment_number, "Segment too short"); return -1; } jbig2_get_region_segment_info(&rsi, segment_data); jbig2_error(ctx, JBIG2_SEVERITY_INFO, sh->segment_number, "generic region: %d x %d @ (%d, %d), flags = %02x", rsi.width, rsi.height, rsi.x, rsi.y, rsi.flags); /* 7.4.6.2 */ seg_flags = segment_data[17]; jbig2_error(ctx, JBIG2_SEVERITY_INFO, sh->segment_number, "segment flags = %02x", seg_flags); if ((seg_flags & 1) && (seg_flags & 6)) jbig2_error(ctx, JBIG2_SEVERITY_WARNING, sh->segment_number, "MMR is 1, but GBTEMPLATE is not 0"); /* 7.4.6.3 */ if (!(seg_flags & 1)) { gbat_bytes = (seg_flags & 6) ? 2 : 8; if (18 + gbat_bytes > sh->data_length) return -1; memcpy(gbat, segment_data + 18, gbat_bytes); jbig2_error(ctx, JBIG2_SEVERITY_INFO, sh->segment_number, "gbat: %d, %d", gbat[0], gbat[1]); } offset = 18 + gbat_bytes; /* Table 34 */ params.MMR = seg_flags & 1; params.GBTEMPLATE = (seg_flags & 6) >> 1; params.TPGDON = (seg_flags & 8) >> 3; params.USESKIP = 0; params.GBW = rsi.width; params.GBH = rsi.height; memcpy (params.gbat, gbat, gbat_bytes); gbreg = jbig2_alloc(ctx->allocator, ((rsi.width + 7) >> 3) * rsi.height); code = jbig2_decode_generic_region(ctx, sh->segment_number, ¶ms, segment_data + offset, sh->data_length - offset, gbreg); /* todo: stash gbreg as segment result */ return code; }