346 lines
8.0 KiB
C
346 lines
8.0 KiB
C
|
#include "mupdf/fitz.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <jpeglib.h>
|
||
|
|
||
|
#ifndef SHARE_JPEG
|
||
|
typedef void * backing_store_ptr;
|
||
|
#include "jmemcust.h"
|
||
|
#endif
|
||
|
|
||
|
typedef struct fz_dctd_s fz_dctd;
|
||
|
|
||
|
struct fz_dctd_s
|
||
|
{
|
||
|
fz_stream *chain;
|
||
|
fz_stream *jpegtables;
|
||
|
fz_stream *curr_stm;
|
||
|
fz_context *ctx;
|
||
|
int color_transform;
|
||
|
int init;
|
||
|
int stride;
|
||
|
int l2factor;
|
||
|
unsigned char *scanline;
|
||
|
unsigned char *rp, *wp;
|
||
|
struct jpeg_decompress_struct cinfo;
|
||
|
struct jpeg_source_mgr srcmgr;
|
||
|
struct jpeg_error_mgr errmgr;
|
||
|
jmp_buf jb;
|
||
|
char msg[JMSG_LENGTH_MAX];
|
||
|
|
||
|
unsigned char buffer[4096];
|
||
|
};
|
||
|
|
||
|
#ifdef SHARE_JPEG
|
||
|
|
||
|
#define JZ_DCT_STATE_FROM_CINFO(c) (fz_dctd *)((c)->client_data)
|
||
|
|
||
|
static void fz_dct_mem_init(struct jpeg_decompress_struct *cinfo, fz_dctd *state)
|
||
|
{
|
||
|
cinfo->client_data = state;
|
||
|
}
|
||
|
|
||
|
#define fz_dct_mem_term(cinfo)
|
||
|
|
||
|
#else /* SHARE_JPEG */
|
||
|
|
||
|
#define JZ_DCT_STATE_FROM_CINFO(c) (fz_dctd *)(GET_CUST_MEM_DATA(c)->priv)
|
||
|
|
||
|
static void *
|
||
|
fz_dct_mem_alloc(j_common_ptr cinfo, size_t size)
|
||
|
{
|
||
|
fz_dctd *state = JZ_DCT_STATE_FROM_CINFO(cinfo);
|
||
|
return fz_malloc_no_throw(state->ctx, size);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
fz_dct_mem_free(j_common_ptr cinfo, void *object, size_t size)
|
||
|
{
|
||
|
fz_dctd *state = JZ_DCT_STATE_FROM_CINFO(cinfo);
|
||
|
fz_free(state->ctx, object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
fz_dct_mem_init(struct jpeg_decompress_struct *cinfo, fz_dctd *state)
|
||
|
{
|
||
|
jpeg_cust_mem_data *custmptr;
|
||
|
custmptr = fz_malloc_struct(state->ctx, jpeg_cust_mem_data);
|
||
|
if (!jpeg_cust_mem_init(custmptr, (void *) state, NULL, NULL, NULL,
|
||
|
fz_dct_mem_alloc, fz_dct_mem_free,
|
||
|
fz_dct_mem_alloc, fz_dct_mem_free, NULL))
|
||
|
{
|
||
|
fz_free(state->ctx, custmptr);
|
||
|
fz_throw(state->ctx, FZ_ERROR_GENERIC, "cannot initialize custom JPEG memory handler");
|
||
|
}
|
||
|
cinfo->client_data = custmptr;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
fz_dct_mem_term(struct jpeg_decompress_struct *cinfo)
|
||
|
{
|
||
|
if (cinfo->client_data)
|
||
|
{
|
||
|
fz_dctd *state = JZ_DCT_STATE_FROM_CINFO(cinfo);
|
||
|
fz_free(state->ctx, cinfo->client_data);
|
||
|
cinfo->client_data = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif /* SHARE_JPEG */
|
||
|
|
||
|
static void error_exit_dct(j_common_ptr cinfo)
|
||
|
{
|
||
|
char msg[JMSG_LENGTH_MAX];
|
||
|
fz_dctd *state = JZ_DCT_STATE_FROM_CINFO(cinfo);
|
||
|
fz_context *ctx = state->ctx;
|
||
|
cinfo->err->format_message(cinfo, msg);
|
||
|
fz_throw(ctx, FZ_ERROR_GENERIC, "jpeg error: %s", msg);
|
||
|
}
|
||
|
|
||
|
static void init_source_dct(j_decompress_ptr cinfo)
|
||
|
{
|
||
|
/* nothing to do */
|
||
|
}
|
||
|
|
||
|
static void term_source_dct(j_decompress_ptr cinfo)
|
||
|
{
|
||
|
/* nothing to do */
|
||
|
}
|
||
|
|
||
|
static boolean fill_input_buffer_dct(j_decompress_ptr cinfo)
|
||
|
{
|
||
|
struct jpeg_source_mgr *src = cinfo->src;
|
||
|
fz_dctd *state = JZ_DCT_STATE_FROM_CINFO(cinfo);
|
||
|
fz_context *ctx = state->ctx;
|
||
|
fz_stream *curr_stm = state->curr_stm;
|
||
|
|
||
|
curr_stm->rp = curr_stm->wp;
|
||
|
fz_try(ctx)
|
||
|
{
|
||
|
src->bytes_in_buffer = fz_available(ctx, curr_stm, 1);
|
||
|
}
|
||
|
fz_catch(ctx)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
src->next_input_byte = curr_stm->rp;
|
||
|
|
||
|
if (src->bytes_in_buffer == 0)
|
||
|
{
|
||
|
static unsigned char eoi[2] = { 0xFF, JPEG_EOI };
|
||
|
fz_warn(state->ctx, "premature end of file in jpeg");
|
||
|
src->next_input_byte = eoi;
|
||
|
src->bytes_in_buffer = 2;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void skip_input_data_dct(j_decompress_ptr cinfo, long num_bytes)
|
||
|
{
|
||
|
struct jpeg_source_mgr *src = cinfo->src;
|
||
|
if (num_bytes > 0)
|
||
|
{
|
||
|
while ((size_t)num_bytes > src->bytes_in_buffer)
|
||
|
{
|
||
|
num_bytes -= (long)src->bytes_in_buffer;
|
||
|
(void) src->fill_input_buffer(cinfo);
|
||
|
}
|
||
|
src->next_input_byte += num_bytes;
|
||
|
src->bytes_in_buffer -= num_bytes;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
next_dctd(fz_context *ctx, fz_stream *stm, size_t max)
|
||
|
{
|
||
|
fz_dctd *state = stm->state;
|
||
|
j_decompress_ptr cinfo = &state->cinfo;
|
||
|
unsigned char *p = state->buffer;
|
||
|
unsigned char *ep;
|
||
|
int c;
|
||
|
|
||
|
if (max > sizeof(state->buffer))
|
||
|
max = sizeof(state->buffer);
|
||
|
ep = state->buffer + max;
|
||
|
|
||
|
fz_try(ctx)
|
||
|
{
|
||
|
if (!state->init)
|
||
|
{
|
||
|
state->init = 1;
|
||
|
|
||
|
/* Skip over any stray whitespace at the start of the stream */
|
||
|
while ((c = fz_peek_byte(ctx, state->chain)) == '\n' || c == '\r' || c == ' ')
|
||
|
(void)fz_read_byte(ctx, state->chain);
|
||
|
|
||
|
jpeg_create_decompress(cinfo);
|
||
|
|
||
|
cinfo->src = &state->srcmgr;
|
||
|
cinfo->src->init_source = init_source_dct;
|
||
|
cinfo->src->fill_input_buffer = fill_input_buffer_dct;
|
||
|
cinfo->src->skip_input_data = skip_input_data_dct;
|
||
|
cinfo->src->resync_to_restart = jpeg_resync_to_restart;
|
||
|
cinfo->src->term_source = term_source_dct;
|
||
|
|
||
|
/* optionally load additional JPEG tables first */
|
||
|
if (state->jpegtables)
|
||
|
{
|
||
|
state->curr_stm = state->jpegtables;
|
||
|
cinfo->src->next_input_byte = state->curr_stm->rp;
|
||
|
cinfo->src->bytes_in_buffer = state->curr_stm->wp - state->curr_stm->rp;
|
||
|
jpeg_read_header(cinfo, 0);
|
||
|
state->curr_stm->rp = state->curr_stm->wp - state->cinfo.src->bytes_in_buffer;
|
||
|
state->curr_stm = state->chain;
|
||
|
}
|
||
|
|
||
|
cinfo->src->next_input_byte = state->curr_stm->rp;
|
||
|
cinfo->src->bytes_in_buffer = state->curr_stm->wp - state->curr_stm->rp;
|
||
|
|
||
|
jpeg_read_header(cinfo, 1);
|
||
|
|
||
|
/* default value if ColorTransform is not set */
|
||
|
if (state->color_transform == -1)
|
||
|
{
|
||
|
if (state->cinfo.num_components == 3)
|
||
|
state->color_transform = 1;
|
||
|
else
|
||
|
state->color_transform = 0;
|
||
|
}
|
||
|
|
||
|
if (cinfo->saw_Adobe_marker)
|
||
|
state->color_transform = cinfo->Adobe_transform;
|
||
|
|
||
|
/* Guess the input colorspace, and set output colorspace accordingly */
|
||
|
switch (cinfo->num_components)
|
||
|
{
|
||
|
case 3:
|
||
|
if (state->color_transform)
|
||
|
cinfo->jpeg_color_space = JCS_YCbCr;
|
||
|
else
|
||
|
cinfo->jpeg_color_space = JCS_RGB;
|
||
|
break;
|
||
|
case 4:
|
||
|
if (state->color_transform)
|
||
|
cinfo->jpeg_color_space = JCS_YCCK;
|
||
|
else
|
||
|
cinfo->jpeg_color_space = JCS_CMYK;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
cinfo->scale_num = 8/(1<<state->l2factor);
|
||
|
cinfo->scale_denom = 8;
|
||
|
|
||
|
jpeg_start_decompress(cinfo);
|
||
|
|
||
|
state->stride = cinfo->output_width * cinfo->output_components;
|
||
|
state->scanline = fz_malloc(ctx, state->stride);
|
||
|
state->rp = state->scanline;
|
||
|
state->wp = state->scanline;
|
||
|
}
|
||
|
|
||
|
while (state->rp < state->wp && p < ep)
|
||
|
*p++ = *state->rp++;
|
||
|
|
||
|
while (p < ep)
|
||
|
{
|
||
|
if (cinfo->output_scanline == cinfo->output_height)
|
||
|
break;
|
||
|
|
||
|
if (p + state->stride <= ep)
|
||
|
{
|
||
|
jpeg_read_scanlines(cinfo, &p, 1);
|
||
|
p += state->stride;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
jpeg_read_scanlines(cinfo, &state->scanline, 1);
|
||
|
state->rp = state->scanline;
|
||
|
state->wp = state->scanline + state->stride;
|
||
|
}
|
||
|
|
||
|
while (state->rp < state->wp && p < ep)
|
||
|
*p++ = *state->rp++;
|
||
|
}
|
||
|
stm->rp = state->buffer;
|
||
|
stm->wp = p;
|
||
|
stm->pos += (p - state->buffer);
|
||
|
}
|
||
|
fz_catch(ctx)
|
||
|
{
|
||
|
if (cinfo->src)
|
||
|
state->curr_stm->rp = state->curr_stm->wp - cinfo->src->bytes_in_buffer;
|
||
|
fz_rethrow(ctx);
|
||
|
}
|
||
|
|
||
|
if (p == stm->rp)
|
||
|
return EOF;
|
||
|
|
||
|
return *stm->rp++;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
close_dctd(fz_context *ctx, void *state_)
|
||
|
{
|
||
|
fz_dctd *state = (fz_dctd *)state_;
|
||
|
|
||
|
if (state->init)
|
||
|
{
|
||
|
/* We call jpeg_abort rather than the more usual
|
||
|
* jpeg_finish_decompress here. This has the same effect,
|
||
|
* but doesn't spew warnings if we didn't read enough data etc.
|
||
|
* Annoyingly jpeg_abort can throw
|
||
|
*/
|
||
|
fz_try(ctx)
|
||
|
jpeg_abort((j_common_ptr)&state->cinfo);
|
||
|
fz_catch(ctx)
|
||
|
{
|
||
|
/* Ignore any errors here */
|
||
|
}
|
||
|
|
||
|
jpeg_destroy_decompress(&state->cinfo);
|
||
|
}
|
||
|
|
||
|
fz_dct_mem_term(&state->cinfo);
|
||
|
|
||
|
if (state->cinfo.src)
|
||
|
state->curr_stm->rp = state->curr_stm->wp - state->cinfo.src->bytes_in_buffer;
|
||
|
|
||
|
fz_free(ctx, state->scanline);
|
||
|
fz_drop_stream(ctx, state->chain);
|
||
|
fz_drop_stream(ctx, state->jpegtables);
|
||
|
fz_free(ctx, state);
|
||
|
}
|
||
|
|
||
|
/* Default: color_transform = -1 (unset), l2factor = 0, jpegtables = NULL */
|
||
|
fz_stream *
|
||
|
fz_open_dctd(fz_context *ctx, fz_stream *chain, int color_transform, int l2factor, fz_stream *jpegtables)
|
||
|
{
|
||
|
fz_dctd *state = fz_malloc_struct(ctx, fz_dctd);
|
||
|
j_decompress_ptr cinfo = &state->cinfo;
|
||
|
|
||
|
state->ctx = ctx;
|
||
|
|
||
|
fz_try(ctx)
|
||
|
fz_dct_mem_init(cinfo, state);
|
||
|
fz_catch(ctx)
|
||
|
{
|
||
|
fz_free(ctx, state);
|
||
|
fz_rethrow(ctx);
|
||
|
}
|
||
|
|
||
|
state->color_transform = color_transform;
|
||
|
state->init = 0;
|
||
|
state->l2factor = l2factor;
|
||
|
state->chain = fz_keep_stream(ctx, chain);
|
||
|
state->jpegtables = fz_keep_stream(ctx, jpegtables);
|
||
|
state->curr_stm = state->chain;
|
||
|
|
||
|
cinfo->src = NULL;
|
||
|
cinfo->err = &state->errmgr;
|
||
|
jpeg_std_error(cinfo->err);
|
||
|
cinfo->err->error_exit = error_exit_dct;
|
||
|
|
||
|
return fz_new_stream(ctx, state, next_dctd, close_dctd);
|
||
|
}
|