#include "fitz_base.h" #include "fitz_stream.h" #include "filt_dctc.h" typedef struct fz_dcte_s fz_dcte; struct mydstmgr { struct jpeg_destination_mgr super; fz_buffer *buf; }; struct fz_dcte_s { fz_filter super; struct jpeg_compress_struct cinfo; struct mydstmgr dst; struct myerrmgr err; int stage; int columns; int rows; int colors; }; static void myinitdest(j_compress_ptr cinfo) { /* empty */ } static boolean myemptybuf(j_compress_ptr cinfo) { return FALSE; } static void mytermdest(j_compress_ptr cinfo) { /* empty */ } fz_error * fz_newdcte(fz_filter **fp, fz_obj *params) { fz_error *err; fz_obj *obj; int i; FZ_NEWFILTER(fz_dcte, e, dcte); e->stage = 0; obj = fz_dictgets(params, "Columns"); if (!obj) { fz_free(e); return fz_throw("missing Columns parameter"); } e->columns = fz_toint(obj); obj = fz_dictgets(params, "Rows"); if (!obj) { fz_free(e); return fz_throw("missing Rows parameter"); } e->rows = fz_toint(obj); obj = fz_dictgets(params, "Colors"); if (!obj) { fz_free(e); return fz_throw("missing Colors parameter"); } e->colors = fz_toint(obj); /* setup error callback first thing */ myiniterr(&e->err); e->cinfo.err = (struct jpeg_error_mgr*) &e->err; if (setjmp(e->err.jb)) { err = fz_throw("cannot encode jpeg: %s", e->err.msg); fz_free(e); return err; } jpeg_create_compress(&e->cinfo); /* prepare destination manager */ e->cinfo.dest = (struct jpeg_destination_mgr *) &e->dst; e->dst.super.init_destination = myinitdest; e->dst.super.empty_output_buffer = myemptybuf; e->dst.super.term_destination = mytermdest; e->dst.super.next_output_byte = nil; e->dst.super.free_in_buffer = 0; /* prepare required encoding options */ e->cinfo.image_width = e->columns; e->cinfo.image_height = e->rows; e->cinfo.input_components = e->colors; switch (e->colors) { case 1: e->cinfo.in_color_space = JCS_GRAYSCALE; break; case 3: e->cinfo.in_color_space = JCS_RGB; break; case 4: e->cinfo.in_color_space = JCS_CMYK; break; default: e->cinfo.in_color_space = JCS_UNKNOWN; break; } jpeg_set_defaults(&e->cinfo); /* FIXME check this */ obj = fz_dictgets(params, "ColorTransform"); if (obj) { int colortransform = fz_toint(obj); if (e->colors == 3 && !colortransform) jpeg_set_colorspace(&e->cinfo, JCS_RGB); if (e->colors == 4 && colortransform) jpeg_set_colorspace(&e->cinfo, JCS_YCCK); if (e->colors == 4 && !colortransform) jpeg_set_colorspace(&e->cinfo, JCS_CMYK); } obj = fz_dictgets(params, "HSamples"); if (obj && fz_isarray(obj)) { fz_obj *o; for (i = 0; i < e->colors; i++) { o = fz_arrayget(obj, i); e->cinfo.comp_info[i].h_samp_factor = fz_toint(o); } } obj = fz_dictgets(params, "VSamples"); if (obj && fz_isarray(obj)) { fz_obj *o; for (i = 0; i < e->colors; i++) { o = fz_arrayget(obj, i); e->cinfo.comp_info[i].v_samp_factor = fz_toint(o); } } /* TODO: quant-tables and huffman-tables */ return fz_okay; } void fz_dropdcte(fz_filter *filter) { fz_dcte *e = (fz_dcte*)filter; if (setjmp(e->err.jb)) { fz_warn("jpeg error: jpeg_destroy_compress: %s", e->err.msg); return; } jpeg_destroy_compress(&e->cinfo); } /* Adobe says zigzag order. IJG > v6a says natural order. */ #if JPEG_LIB_VERSION >= 61 #define unzigzag(x) unzigzagorder[x] /* zigzag array position of n'th element of natural array order */ static const unsigned char unzigzagorder[] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; #else #define unzigzag(x) (x) #endif fz_error * fz_setquanttables(fz_dcte *e, unsigned int **qtables, int qfactor) { int i, j; unsigned int table[64]; if (setjmp(e->err.jb)) { return fz_throw("jpeg error: jpeg_add_quant_table: %s", e->err.msg); } /* TODO: check for duplicate tables */ for (i = 0; i < e->colors; i++) { for (j = 0; j < 64; j++) { table[j] = unzigzag(qtables[i][j]); } jpeg_add_quant_table(&e->cinfo, i, table, qfactor, TRUE); e->cinfo.comp_info[i].quant_tbl_no = i; } return fz_okay; } fz_error * fz_processdcte(fz_filter *filter, fz_buffer *in, fz_buffer *out) { fz_dcte *e = (fz_dcte*)filter; JSAMPROW scanline[1]; int stride; int i; e->dst.buf = out; e->dst.super.free_in_buffer = out->ep - out->wp; e->dst.super.next_output_byte = out->wp; if (setjmp(e->err.jb)) { return fz_throw("cannot encode jpeg: %s", e->err.msg); } switch (e->stage) { case 0: /* must have enough space for markers, typically 600 bytes or so */ if (out->wp + 1024 > out->ep) goto needoutput; jpeg_start_compress(&e->cinfo, TRUE); /* TODO: write Adobe ColorTransform marker */ /* fall through */ e->stage = 1; case 1: stride = e->columns * e->colors; while (e->cinfo.next_scanline < e->cinfo.image_height) { if (in->rp + stride > in->wp) goto needinput; scanline[0] = in->rp; i = jpeg_write_scanlines(&e->cinfo, scanline, 1); if (i == 0) goto needoutput; in->rp += stride; } /* fall through */ e->stage = 2; case 2: /* must have enough space for end markers */ if (out->wp + 100 > out->ep) goto needoutput; /* finish compress cannot suspend! */ jpeg_finish_compress(&e->cinfo); e->stage = 3; out->wp = out->ep - e->dst.super.free_in_buffer; return fz_iodone; } needinput: out->wp = out->ep - e->dst.super.free_in_buffer; return fz_ioneedin; needoutput: out->wp = out->ep - e->dst.super.free_in_buffer; return fz_ioneedout; }