summaryrefslogtreecommitdiff
path: root/jcmaster.c
diff options
context:
space:
mode:
Diffstat (limited to 'jcmaster.c')
-rw-r--r--jcmaster.c416
1 files changed, 301 insertions, 115 deletions
diff --git a/jcmaster.c b/jcmaster.c
index 6e6a11a..84494e6 100644
--- a/jcmaster.c
+++ b/jcmaster.c
@@ -6,9 +6,9 @@
* For conditions of distribution and use, see the accompanying README file.
*
* This file contains master control logic for the JPEG compressor.
- * These routines are concerned with selecting the modules to be executed
- * and with determining the number of passes and the work to be done in each
- * pass.
+ * These routines are concerned with parameter validation, initial setup,
+ * and inter-pass control (determining the number of passes and the work
+ * to be done in each pass).
*/
#define JPEG_INTERNALS
@@ -18,10 +18,21 @@
/* Private state */
+typedef enum {
+ main_pass, /* input data, also do first output step */
+ huff_opt_pass, /* Huffman code optimization pass */
+ output_pass /* data output pass */
+} c_pass_type;
+
typedef struct {
struct jpeg_comp_master pub; /* public fields */
- int pass_number; /* eventually need more complex state... */
+ c_pass_type pass_type; /* the type of the current pass */
+
+ int pass_number; /* # of passes completed */
+ int total_passes; /* total # of passes needed */
+
+ int scan_number; /* current index in scan_info[] */
} my_comp_master;
typedef my_comp_master * my_master_ptr;
@@ -82,6 +93,8 @@ initial_setup (j_compress_ptr cinfo)
/* Compute dimensions of components */
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
ci++, compptr++) {
+ /* Fill in the correct component_index value; don't rely on application */
+ compptr->component_index = ci;
/* For compression, we never do DCT scaling. */
compptr->DCT_scaled_size = DCTSIZE;
/* Size in DCT blocks */
@@ -111,6 +124,174 @@ initial_setup (j_compress_ptr cinfo)
}
+#ifdef C_MULTISCAN_FILES_SUPPORTED
+
+LOCAL void
+validate_script (j_compress_ptr cinfo)
+/* Verify that the scan script in cinfo->scan_info[] is valid; also
+ * determine whether it uses progressive JPEG, and set cinfo->progressive_mode.
+ */
+{
+ const jpeg_scan_info * scanptr;
+ int scanno, ncomps, ci, coefi, thisi;
+ int Ss, Se, Ah, Al;
+ boolean component_sent[MAX_COMPONENTS];
+#ifdef C_PROGRESSIVE_SUPPORTED
+ int * last_bitpos_ptr;
+ int last_bitpos[MAX_COMPONENTS][DCTSIZE2];
+ /* -1 until that coefficient has been seen; then last Al for it */
+#endif
+
+ if (cinfo->num_scans <= 0)
+ ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0);
+
+ /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1;
+ * for progressive JPEG, no scan can have this.
+ */
+ scanptr = cinfo->scan_info;
+ if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) {
+#ifdef C_PROGRESSIVE_SUPPORTED
+ cinfo->progressive_mode = TRUE;
+ last_bitpos_ptr = & last_bitpos[0][0];
+ for (ci = 0; ci < cinfo->num_components; ci++)
+ for (coefi = 0; coefi < DCTSIZE2; coefi++)
+ *last_bitpos_ptr++ = -1;
+#else
+ ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+ } else {
+ cinfo->progressive_mode = FALSE;
+ for (ci = 0; ci < cinfo->num_components; ci++)
+ component_sent[ci] = FALSE;
+ }
+
+ for (scanno = 1; scanno <= cinfo->num_scans; scanptr++, scanno++) {
+ /* Validate component indexes */
+ ncomps = scanptr->comps_in_scan;
+ if (ncomps <= 0 || ncomps > MAX_COMPS_IN_SCAN)
+ ERREXIT2(cinfo, JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN);
+ for (ci = 0; ci < ncomps; ci++) {
+ thisi = scanptr->component_index[ci];
+ if (thisi < 0 || thisi >= cinfo->num_components)
+ ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
+ /* Components must appear in SOF order within each scan */
+ if (ci > 0 && thisi <= scanptr->component_index[ci-1])
+ ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
+ }
+ /* Validate progression parameters */
+ Ss = scanptr->Ss;
+ Se = scanptr->Se;
+ Ah = scanptr->Ah;
+ Al = scanptr->Al;
+ if (cinfo->progressive_mode) {
+#ifdef C_PROGRESSIVE_SUPPORTED
+ if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 ||
+ Ah < 0 || Ah > 13 || Al < 0 || Al > 13)
+ ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+ if (Ss == 0) {
+ if (Se != 0) /* DC and AC together not OK */
+ ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+ } else {
+ if (ncomps != 1) /* AC scans must be for only one component */
+ ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+ }
+ for (ci = 0; ci < ncomps; ci++) {
+ last_bitpos_ptr = & last_bitpos[scanptr->component_index[ci]][0];
+ if (Ss != 0 && last_bitpos_ptr[0] < 0) /* AC without prior DC scan */
+ ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+ for (coefi = Ss; coefi <= Se; coefi++) {
+ if (last_bitpos_ptr[coefi] < 0) {
+ /* first scan of this coefficient */
+ if (Ah != 0)
+ ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+ } else {
+ /* not first scan */
+ if (Ah != last_bitpos_ptr[coefi] || Al != Ah-1)
+ ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+ }
+ last_bitpos_ptr[coefi] = Al;
+ }
+ }
+#endif
+ } else {
+ /* For sequential JPEG, all progression parameters must be these: */
+ if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0)
+ ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+ /* Make sure components are not sent twice */
+ for (ci = 0; ci < ncomps; ci++) {
+ thisi = scanptr->component_index[ci];
+ if (component_sent[thisi])
+ ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
+ component_sent[thisi] = TRUE;
+ }
+ }
+ }
+
+ /* Now verify that everything got sent. */
+ if (cinfo->progressive_mode) {
+#ifdef C_PROGRESSIVE_SUPPORTED
+ /* For progressive mode, we only check that at least some DC data
+ * got sent for each component; the spec does not require that all bits
+ * of all coefficients be transmitted. Would it be wiser to enforce
+ * transmission of all coefficient bits??
+ */
+ for (ci = 0; ci < cinfo->num_components; ci++) {
+ if (last_bitpos[ci][0] < 0)
+ ERREXIT(cinfo, JERR_MISSING_DATA);
+ }
+#endif
+ } else {
+ for (ci = 0; ci < cinfo->num_components; ci++) {
+ if (! component_sent[ci])
+ ERREXIT(cinfo, JERR_MISSING_DATA);
+ }
+ }
+}
+
+#endif /* C_MULTISCAN_FILES_SUPPORTED */
+
+
+LOCAL void
+select_scan_parameters (j_compress_ptr cinfo)
+/* Set up the scan parameters for the current scan */
+{
+ int ci;
+
+#ifdef C_MULTISCAN_FILES_SUPPORTED
+ if (cinfo->scan_info != NULL) {
+ /* Prepare for current scan --- the script is already validated */
+ my_master_ptr master = (my_master_ptr) cinfo->master;
+ const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number;
+
+ cinfo->comps_in_scan = scanptr->comps_in_scan;
+ for (ci = 0; ci < scanptr->comps_in_scan; ci++) {
+ cinfo->cur_comp_info[ci] =
+ &cinfo->comp_info[scanptr->component_index[ci]];
+ }
+ cinfo->Ss = scanptr->Ss;
+ cinfo->Se = scanptr->Se;
+ cinfo->Ah = scanptr->Ah;
+ cinfo->Al = scanptr->Al;
+ }
+ else
+#endif
+ {
+ /* Prepare for single sequential-JPEG scan containing all components */
+ if (cinfo->num_components > MAX_COMPS_IN_SCAN)
+ ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
+ MAX_COMPS_IN_SCAN);
+ cinfo->comps_in_scan = cinfo->num_components;
+ for (ci = 0; ci < cinfo->num_components; ci++) {
+ cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci];
+ }
+ cinfo->Ss = 0;
+ cinfo->Se = DCTSIZE2-1;
+ cinfo->Ah = 0;
+ cinfo->Al = 0;
+ }
+}
+
+
LOCAL void
per_scan_setup (j_compress_ptr cinfo)
/* Do computations that are needed before processing a JPEG scan */
@@ -178,7 +359,7 @@ per_scan_setup (j_compress_ptr cinfo)
compptr->last_row_height = tmp;
/* Prepare array describing MCU composition */
mcublks = compptr->MCU_blocks;
- if (cinfo->blocks_in_MCU + mcublks > MAX_BLOCKS_IN_MCU)
+ if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU)
ERREXIT(cinfo, JERR_BAD_MCU_SIZE);
while (mcublks-- > 0) {
cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci;
@@ -197,59 +378,6 @@ per_scan_setup (j_compress_ptr cinfo)
/*
- * Master selection of compression modules.
- * This is done once at the start of processing an image. We determine
- * which modules will be used and give them appropriate initialization calls.
- */
-
-LOCAL void
-master_selection (j_compress_ptr cinfo)
-{
- my_master_ptr master = (my_master_ptr) cinfo->master;
-
- initial_setup(cinfo);
- master->pass_number = 0;
-
- /* There's not a lot of smarts here right now, but it'll get more
- * complicated when we have multiple implementations available...
- */
-
- /* Preprocessing */
- if (! cinfo->raw_data_in) {
- jinit_color_converter(cinfo);
- jinit_downsampler(cinfo);
- jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */);
- }
- /* Forward DCT */
- jinit_forward_dct(cinfo);
- /* Entropy encoding: either Huffman or arithmetic coding. */
- if (cinfo->arith_code) {
-#ifdef C_ARITH_CODING_SUPPORTED
- jinit_arith_encoder(cinfo);
-#else
- ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
-#endif
- } else
- jinit_huff_encoder(cinfo);
-
- /* For now, a full buffer is needed only for Huffman optimization. */
- jinit_c_coef_controller(cinfo, cinfo->optimize_coding);
- jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */);
-
- jinit_marker_writer(cinfo);
-
- /* We can now tell the memory manager to allocate virtual arrays. */
- (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
-
- /* Write the datastream header (SOI) immediately.
- * Frame and scan headers are postponed till later.
- * This lets application insert special markers after the SOI.
- */
- (*cinfo->marker->write_file_header) (cinfo);
-}
-
-
-/*
* Per-pass setup.
* This is called at the beginning of each pass. We determine which modules
* will be active during this pass and give them appropriate start_pass calls.
@@ -261,75 +389,77 @@ METHODDEF void
prepare_for_pass (j_compress_ptr cinfo)
{
my_master_ptr master = (my_master_ptr) cinfo->master;
- int ci;
- int npasses;
-
- /* ???? JUST A QUICK CROCK FOR NOW ??? */
-
- /* For now, handle only single interleaved output scan; */
- /* we support two passes for Huffman optimization. */
-
- /* Prepare for single scan containing all components */
- if (cinfo->num_components > MAX_COMPS_IN_SCAN)
- ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
- MAX_COMPS_IN_SCAN);
- cinfo->comps_in_scan = cinfo->num_components;
- for (ci = 0; ci < cinfo->num_components; ci++) {
- cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci];
- }
- per_scan_setup(cinfo);
-
- if (! cinfo->optimize_coding) {
- /* Standard single-pass case */
- npasses = 1;
- master->pub.call_pass_startup = TRUE;
- master->pub.is_last_pass = TRUE;
+ switch (master->pass_type) {
+ case main_pass:
+ /* Initial pass: will collect input data, and do either Huffman
+ * optimization or data output for the first scan.
+ */
+ select_scan_parameters(cinfo);
+ per_scan_setup(cinfo);
if (! cinfo->raw_data_in) {
(*cinfo->cconvert->start_pass) (cinfo);
(*cinfo->downsample->start_pass) (cinfo);
(*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU);
}
(*cinfo->fdct->start_pass) (cinfo);
- (*cinfo->entropy->start_pass) (cinfo, FALSE);
- (*cinfo->coef->start_pass) (cinfo, JBUF_PASS_THRU);
+ (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding);
+ (*cinfo->coef->start_pass) (cinfo,
+ (master->total_passes > 1 ?
+ JBUF_SAVE_AND_PASS : JBUF_PASS_THRU));
(*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU);
- } else {
- npasses = 2;
- switch (master->pass_number) {
- case 0:
- /* Huffman optimization: run all modules, gather statistics */
+ if (cinfo->optimize_coding) {
+ /* No immediate data output; postpone writing frame/scan headers */
master->pub.call_pass_startup = FALSE;
- master->pub.is_last_pass = FALSE;
- if (! cinfo->raw_data_in) {
- (*cinfo->cconvert->start_pass) (cinfo);
- (*cinfo->downsample->start_pass) (cinfo);
- (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU);
- }
- (*cinfo->fdct->start_pass) (cinfo);
+ } else {
+ /* Will write frame/scan headers at first jpeg_write_scanlines call */
+ master->pub.call_pass_startup = TRUE;
+ }
+ break;
+#ifdef ENTROPY_OPT_SUPPORTED
+ case huff_opt_pass:
+ /* Do Huffman optimization for a scan after the first one. */
+ select_scan_parameters(cinfo);
+ per_scan_setup(cinfo);
+ if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) {
(*cinfo->entropy->start_pass) (cinfo, TRUE);
- (*cinfo->coef->start_pass) (cinfo, JBUF_SAVE_AND_PASS);
- (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU);
- break;
- case 1:
- /* Second pass: reread data from coefficient buffer */
- master->pub.is_last_pass = TRUE;
- (*cinfo->entropy->start_pass) (cinfo, FALSE);
(*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST);
- /* We emit frame/scan headers now */
- (*cinfo->marker->write_frame_header) (cinfo);
- (*cinfo->marker->write_scan_header) (cinfo);
+ master->pub.call_pass_startup = FALSE;
break;
}
+ /* Special case: Huffman DC refinement scans need no Huffman table
+ * and therefore we can skip the optimization pass for them.
+ */
+ master->pass_type = output_pass;
+ master->pass_number++;
+ /*FALLTHROUGH*/
+#endif
+ case output_pass:
+ /* Do a data-output pass. */
+ /* We need not repeat per-scan setup if prior optimization pass did it. */
+ if (! cinfo->optimize_coding) {
+ select_scan_parameters(cinfo);
+ per_scan_setup(cinfo);
+ }
+ (*cinfo->entropy->start_pass) (cinfo, FALSE);
+ (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST);
+ /* We emit frame/scan headers now */
+ if (master->scan_number == 0)
+ (*cinfo->marker->write_frame_header) (cinfo);
+ (*cinfo->marker->write_scan_header) (cinfo);
+ master->pub.call_pass_startup = FALSE;
+ break;
+ default:
+ ERREXIT(cinfo, JERR_NOT_COMPILED);
}
+ master->pub.is_last_pass = (master->pass_number == master->total_passes-1);
+
/* Set up progress monitor's pass info if present */
if (cinfo->progress != NULL) {
cinfo->progress->completed_passes = master->pass_number;
- cinfo->progress->total_passes = npasses;
+ cinfo->progress->total_passes = master->total_passes;
}
-
- master->pass_number++;
}
@@ -360,23 +490,45 @@ pass_startup (j_compress_ptr cinfo)
METHODDEF void
finish_pass_master (j_compress_ptr cinfo)
{
- /* More complex logic later ??? */
+ my_master_ptr master = (my_master_ptr) cinfo->master;
- /* The entropy coder needs an end-of-pass call, either to analyze
- * statistics or to flush its output buffer.
+ /* The entropy coder always needs an end-of-pass call,
+ * either to analyze statistics or to flush its output buffer.
*/
(*cinfo->entropy->finish_pass) (cinfo);
+
+ /* Update state for next pass */
+ switch (master->pass_type) {
+ case main_pass:
+ /* next pass is either output of scan 0 (after optimization)
+ * or output of scan 1 (if no optimization).
+ */
+ master->pass_type = output_pass;
+ if (! cinfo->optimize_coding)
+ master->scan_number++;
+ break;
+ case huff_opt_pass:
+ /* next pass is always output of current scan */
+ master->pass_type = output_pass;
+ break;
+ case output_pass:
+ /* next pass is either optimization or output of next scan */
+ if (cinfo->optimize_coding)
+ master->pass_type = huff_opt_pass;
+ master->scan_number++;
+ break;
+ }
+
+ master->pass_number++;
}
/*
* Initialize master compression control.
- * This creates my own subrecord and also performs the master selection phase,
- * which causes other modules to create their subrecords.
*/
GLOBAL void
-jinit_master_compress (j_compress_ptr cinfo)
+jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only)
{
my_master_ptr master;
@@ -387,6 +539,40 @@ jinit_master_compress (j_compress_ptr cinfo)
master->pub.prepare_for_pass = prepare_for_pass;
master->pub.pass_startup = pass_startup;
master->pub.finish_pass = finish_pass_master;
+ master->pub.is_last_pass = FALSE;
+
+ /* Validate parameters, determine derived values */
+ initial_setup(cinfo);
- master_selection(cinfo);
+ if (cinfo->scan_info != NULL) {
+#ifdef C_MULTISCAN_FILES_SUPPORTED
+ validate_script(cinfo);
+#else
+ ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+ } else {
+ cinfo->progressive_mode = FALSE;
+ cinfo->num_scans = 1;
+ }
+
+ if (cinfo->progressive_mode) /* TEMPORARY HACK ??? */
+ cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */
+
+ /* Initialize my private state */
+ if (transcode_only) {
+ /* no main pass in transcoding */
+ if (cinfo->optimize_coding)
+ master->pass_type = huff_opt_pass;
+ else
+ master->pass_type = output_pass;
+ } else {
+ /* for normal compression, first pass is always this type: */
+ master->pass_type = main_pass;
+ }
+ master->scan_number = 0;
+ master->pass_number = 0;
+ if (cinfo->optimize_coding)
+ master->total_passes = cinfo->num_scans * 2;
+ else
+ master->total_passes = cinfo->num_scans;
}