summaryrefslogtreecommitdiff
path: root/gs/base/sjbig2.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/sjbig2.c')
-rw-r--r--gs/base/sjbig2.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/gs/base/sjbig2.c b/gs/base/sjbig2.c
new file mode 100644
index 000000000..4716c5ded
--- /dev/null
+++ b/gs/base/sjbig2.c
@@ -0,0 +1,256 @@
+/* Copyright (C) 2001-2006 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.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, for further information.
+*/
+
+/* $Id$ */
+/* jbig2decode filter implementation -- hooks in libjbig2dec */
+
+#include "stdint_.h"
+#include "memory_.h"
+#include "stdio_.h" /* sprintf() for debug output */
+
+#include "gserrors.h"
+#include "gserror.h"
+#include "gdebug.h"
+#include "strimpl.h"
+#include "sjbig2.h"
+
+/* stream implementation */
+
+/* The /JBIG2Decode filter is a fairly memory intensive one to begin with,
+ particularly in the initial jbig2dec library implementation. Furthermore,
+ as a PDF 1.4 feature, we can assume a fairly large (host-level) machine.
+ We therefore dispense with the normal Ghostscript memory discipline and
+ let the library allocate all its resources on the heap. The pointers to
+ these are not enumerated and so will not be garbage collected. We rely
+ on our release() proc being called to deallocate state.
+ */
+
+private_st_jbig2decode_state(); /* creates a gc object for our state, defined in sjbig2.h */
+
+/* error callback for jbig2 decoder */
+static int
+s_jbig2decode_error(void *error_callback_data, const char *msg, Jbig2Severity severity,
+ int32_t seg_idx)
+{
+ stream_jbig2decode_state *const state =
+ (stream_jbig2decode_state *) error_callback_data;
+ const char *type;
+ char segment[22];
+ int code = 0;
+
+ switch (severity) {
+ case JBIG2_SEVERITY_DEBUG:
+ type = "DEBUG"; break;;
+ case JBIG2_SEVERITY_INFO:
+ type = "info"; break;;
+ case JBIG2_SEVERITY_WARNING:
+ type = "WARNING"; break;;
+ case JBIG2_SEVERITY_FATAL:
+ type = "FATAL ERROR decoding image:";
+ /* pass the fatal error upstream if possible */
+ code = gs_error_ioerror;
+ if (state != NULL) state->error = code;
+ break;;
+ default: type = "unknown message:"; break;;
+ }
+ if (seg_idx == -1) segment[0] = '\0';
+ else sprintf(segment, "(segment 0x%02x)", seg_idx);
+
+ if (severity == JBIG2_SEVERITY_FATAL) {
+ dlprintf3("jbig2dec %s %s %s\n", type, msg, segment);
+ } else {
+ if_debug3('w', "[w] jbig2dec %s %s %s\n", type, msg, segment);
+ }
+
+ return code;
+}
+
+/* invert the bits in a buffer */
+/* jbig2 and postscript have different senses of what pixel
+ value is black, so we must invert the image */
+static void
+s_jbig2decode_invert_buffer(unsigned char *buf, int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++)
+ *buf++ ^= 0xFF;
+}
+
+/* parse a globals stream packed into a gs_bytestring for us by the postscript
+ layer and stuff the resulting context into a pointer for use in later decoding */
+int
+s_jbig2decode_make_global_data(byte *data, uint length, void **result)
+{
+ Jbig2Ctx *ctx = NULL;
+ int code;
+
+ /* the cvision encoder likes to include empty global streams */
+ if (length == 0) {
+ if_debug0('w', "[w] ignoring zero-length jbig2 global stream.\n");
+ *result = NULL;
+ return 0;
+ }
+
+ /* allocate a context with which to parse our global segments */
+ ctx = jbig2_ctx_new(NULL, JBIG2_OPTIONS_EMBEDDED, NULL,
+ s_jbig2decode_error, NULL);
+
+ /* parse the global bitstream */
+ code = jbig2_data_in(ctx, data, length);
+
+ if (code) {
+ /* error parsing the global stream */
+ *result = NULL;
+ return code;
+ }
+
+ /* canonize and store our global state */
+ *result = jbig2_make_global_ctx(ctx);
+
+ return 0; /* todo: check for allocation failure */
+}
+
+/* release a global ctx pointer */
+void
+s_jbig2decode_free_global_data(void *data)
+{
+ Jbig2GlobalCtx *global_ctx = (Jbig2GlobalCtx*)data;
+
+ jbig2_global_ctx_free(global_ctx);
+}
+
+/* store a global ctx pointer in our state structure */
+int
+s_jbig2decode_set_global_data(stream_state *ss, s_jbig2_global_data_t *gd)
+{
+ stream_jbig2decode_state *state = (stream_jbig2decode_state*)ss;
+ state->global_struct = gd;
+ state->global_ctx = (Jbig2GlobalCtx*)(gd ? gd->data : 0);
+ return 0;
+}
+
+/* initialize the steam.
+ this involves allocating the context structures, and
+ initializing the global context from the /JBIG2Globals object reference
+ */
+static int
+s_jbig2decode_init(stream_state * ss)
+{
+ stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss;
+ Jbig2GlobalCtx *global_ctx = state->global_ctx; /* may be NULL */
+
+ /* initialize the decoder with the parsed global context if any */
+ state->decode_ctx = jbig2_ctx_new(NULL, JBIG2_OPTIONS_EMBEDDED,
+ global_ctx, s_jbig2decode_error, ss);
+ state->image = 0;
+ state->error = 0;
+ return 0; /* todo: check for allocation failure */
+}
+
+/* process a section of the input and return any decoded data.
+ see strimpl.h for return codes.
+ */
+static int
+s_jbig2decode_process(stream_state * ss, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss;
+ Jbig2Image *image = state->image;
+ long in_size = pr->limit - pr->ptr;
+ long out_size = pw->limit - pw->ptr;
+ int status = 0;
+
+ /* there will only be a single page image,
+ so pass all data in before looking for any output.
+ note that the gs stream library expects offset-by-one
+ indexing of the buffers, while jbig2dec uses normal 0 indexes */
+ if (in_size > 0) {
+ /* pass all available input to the decoder */
+ jbig2_data_in(state->decode_ctx, pr->ptr + 1, in_size);
+ pr->ptr += in_size;
+ /* simulate end-of-page segment */
+ if (last == 1) {
+ jbig2_complete_page(state->decode_ctx);
+ }
+ /* handle fatal decoding errors reported through our callback */
+ if (state->error) return state->error;
+ }
+ if (out_size > 0) {
+ if (image == NULL) {
+ /* see if a page image in available */
+ image = jbig2_page_out(state->decode_ctx);
+ if (image != NULL) {
+ state->image = image;
+ state->offset = 0;
+ }
+ }
+ if (image != NULL) {
+ /* copy data out of the decoded image, if any */
+ long image_size = image->height*image->stride;
+ long usable = min(image_size - state->offset, out_size);
+ memcpy(pw->ptr + 1, image->data + state->offset, usable);
+ s_jbig2decode_invert_buffer(pw->ptr + 1, usable);
+ state->offset += usable;
+ pw->ptr += usable;
+ status = (state->offset < image_size) ? 1 : 0;
+ }
+ }
+
+ return status;
+}
+
+/* stream release.
+ free all our decoder state.
+ */
+static void
+s_jbig2decode_release(stream_state *ss)
+{
+ stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss;
+
+ if (state->decode_ctx) {
+ if (state->image) jbig2_release_page(state->decode_ctx, state->image);
+ jbig2_ctx_free(state->decode_ctx);
+ }
+ /* the interpreter takes care of freeing the global_ctx */
+}
+
+/* set stream defaults.
+ this hook exists to avoid confusing the gc with bogus
+ pointers. we use it similarly just to NULL all the pointers.
+ (could just be done in _init?)
+ */
+static void
+s_jbig2decode_set_defaults(stream_state *ss)
+{
+ stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss;
+
+ /* state->global_ctx is not owned by us */
+ state->global_struct = NULL;
+ state->global_ctx = NULL;
+ state->decode_ctx = NULL;
+ state->image = NULL;
+ state->offset = 0;
+ state->error = 0;
+}
+
+
+/* stream template */
+const stream_template s_jbig2decode_template = {
+ &st_jbig2decode_state,
+ s_jbig2decode_init,
+ s_jbig2decode_process,
+ 1, 1, /* min in and out buffer sizes we can handle --should be ~32k,64k for efficiency? */
+ s_jbig2decode_release,
+ s_jbig2decode_set_defaults
+};