/* * jdpipe.c * * Copyright (C) 1991, Thomas G. Lane. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains decompression pipeline controllers. * These routines are invoked via the d_pipeline_controller method. * * There are four basic pipeline controllers, one for each combination of: * single-scan JPEG file (single component or fully interleaved) * vs. multiple-scan JPEG file (noninterleaved or partially interleaved). * * 2-pass color quantization * vs. no color quantization or 1-pass quantization. * * Note that these conditions determine the needs for "big" images: * multiple scans imply a big image for recombining the color components; * 2-pass color quantization needs a big image for saving the data for pass 2. * * All but the simplest controller (single-scan, no 2-pass quantization) can be * compiled out through configuration options, if you need to make a minimal * implementation. You should leave in multiple-scan support if at all * possible, so that you can handle all legal JPEG files. */ #include "jinclude.h" /* * About the data structures: * * The processing chunk size for unsubsampling is referred to in this file as * a "row group": a row group is defined as Vk (v_samp_factor) sample rows of * any component while subsampled, or Vmax (max_v_samp_factor) unsubsampled * rows. In an interleaved scan each MCU row contains exactly DCTSIZE row * groups of each component in the scan. In a noninterleaved scan an MCU row * is one row of blocks, which might not be an integral number of row groups; * therefore, we read in Vk MCU rows to obtain the same amount of data as we'd * have in an interleaved scan. * To provide context for the unsubsampling step, we have to retain the last * two row groups of the previous MCU row while reading in the next MCU row * (or set of Vk MCU rows). To do this without copying data about, we create * a rather strange data structure. Exactly DCTSIZE+2 row groups of samples * are allocated, but we create two different sets of pointers to this array. * The second set swaps the last two pairs of row groups. By working * alternately with the two sets of pointers, we can access the data in the * desired order. * * Cross-block smoothing also needs context above and below the "current" row. * Since this is an optional feature, I've implemented it in a way that is * much simpler but requires more than the minimum amount of memory. We * simply allocate three extra MCU rows worth of coefficient blocks and use * them to "read ahead" one MCU row in the file. For a typical 1000-pixel-wide * image with 2x2,1x1,1x1 sampling, each MCU row is about 50Kb; an 80x86 * machine may be unable to apply cross-block smoothing to wider images. */ /* * These variables are logically local to the pipeline controller, * but we make them static so that scan_big_image can use them * without having to pass them through the quantization routines. * If you don't support 2-pass quantization, you could make them locals. */ static int rows_in_mem; /* # of sample rows in full-size buffers */ /* Full-size image array holding desubsampled, color-converted data. */ static big_sarray_ptr *fullsize_cnvt_image; static JSAMPIMAGE fullsize_cnvt_ptrs; /* workspace for access_big_sarray() results */ /* Work buffer for color quantization output (full size, only 1 component). */ static JSAMPARRAY quantize_out; /* * Utility routines: common code for pipeline controllers */ LOCAL void interleaved_scan_setup (decompress_info_ptr cinfo) /* Compute all derived info for an interleaved (multi-component) scan */ /* On entry, cinfo->comps_in_scan and cinfo->cur_comp_info[] are set up */ { short ci, mcublks; jpeg_component_info *compptr; if (cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) ERREXIT(cinfo->emethods, "Too many components for interleaved scan"); cinfo->MCUs_per_row = (cinfo->image_width + cinfo->max_h_samp_factor*DCTSIZE - 1) / (cinfo->max_h_samp_factor*DCTSIZE); cinfo->MCU_rows_in_scan = (cinfo->image_height + cinfo->max_v_samp_factor*DCTSIZE - 1) / (cinfo->max_v_samp_factor*DCTSIZE); cinfo->blocks_in_MCU = 0; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; /* for interleaved scan, sampling factors give # of blocks per component */ compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; /* compute physical dimensions of component */ compptr->subsampled_width = jround_up(compptr->true_comp_width, (long) (compptr->MCU_width*DCTSIZE)); compptr->subsampled_height = jround_up(compptr->true_comp_height, (long) (compptr->MCU_height*DCTSIZE)); /* Sanity check */ if (compptr->subsampled_width != (cinfo->MCUs_per_row * (compptr->MCU_width*DCTSIZE))) ERREXIT(cinfo->emethods, "I'm confused about the image width"); /* Prepare array describing MCU composition */ mcublks = compptr->MCU_blocks; if (cinfo->blocks_in_MCU + mcublks > MAX_BLOCKS_IN_MCU) ERREXIT(cinfo->emethods, "Sampling factors too large for interleaved scan"); while (mcublks-- > 0) { cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; } } (*cinfo->methods->d_per_scan_method_selection) (cinfo); } LOCAL void noninterleaved_scan_setup (decompress_info_ptr cinfo) /* Compute all derived info for a noninterleaved (single-component) scan */ /* On entry, cinfo->comps_in_scan = 1 and cinfo->cur_comp_info[0] is set up */ { jpeg_component_info *compptr = cinfo->cur_comp_info[0]; /* for noninterleaved scan, always one block per MCU */ compptr->MCU_width = 1; compptr->MCU_height = 1; compptr->MCU_blocks = 1; /* compute physical dimensions of component */ compptr->subsampled_width = jround_up(compptr->true_comp_width, (long) DCTSIZE); compptr->subsampled_height = jround_up(compptr->true_comp_height, (long) DCTSIZE); cinfo->MCUs_per_row = compptr->subsampled_width / DCTSIZE; cinfo->MCU_rows_in_scan = compptr->subsampled_height / DCTSIZE; /* Prepare array describing MCU composition */ cinfo->blocks_in_MCU = 1; cinfo->MCU_membership[0] = 0; (*cinfo->methods->d_per_scan_method_selection) (cinfo); } LOCAL void reverse_DCT (decompress_info_ptr cinfo, JBLOCKIMAGE coeff_data, JSAMPIMAGE output_data, int start_row) /* Perform inverse DCT on each block in an MCU row's worth of data; */ /* output the results into a sample array starting at row start_row. */ /* NB: start_row can only be nonzero when dealing with a single-component */ /* scan; otherwise we'd have to provide for different offsets for different */ /* components, since the heights of interleaved MCU rows can vary. */ { DCTBLOCK block; JBLOCKROW browptr; JSAMPARRAY srowptr; long blocksperrow, bi; short numrows, ri; short ci; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { /* calc size of an MCU row in this component */ blocksperrow = cinfo->cur_comp_info[ci]->subsampled_width / DCTSIZE; numrows = cinfo->cur_comp_info[ci]->MCU_height; /* iterate through all blocks in MCU row */ for (ri = 0; ri < numrows; ri++) { browptr = coeff_data[ci][ri]; srowptr = output_data[ci] + (ri * DCTSIZE + start_row); for (bi = 0; bi < blocksperrow; bi++) { /* copy the data into a local DCTBLOCK. This allows for change of * representation (if DCTELEM != JCOEF). On 80x86 machines it also * brings the data back from FAR storage to NEAR storage. */ { register JCOEFPTR elemptr = browptr[bi]; register DCTELEM *localblkptr = block; register short elem = DCTSIZE2; while (--elem >= 0) *localblkptr++ = (DCTELEM) *elemptr++; } j_rev_dct(block); /* perform inverse DCT */ /* output the data into the sample array. * Note change from signed to unsigned representation: * DCT calculation works with values +-CENTERJSAMPLE, * but sample arrays always hold 0..MAXJSAMPLE. * Have to do explicit range-limiting because of quantization errors * and so forth in the DCT/IDCT phase. */ { register JSAMPROW elemptr; register DCTELEM *localblkptr = block; register short elemr, elemc; register DCTELEM temp; for (elemr = 0; elemr < DCTSIZE; elemr++) { elemptr = srowptr[elemr] + (bi * DCTSIZE); for (elemc = 0; elemc < DCTSIZE; elemc++) { temp = (*localblkptr++) + CENTERJSAMPLE; if (temp < 0) temp = 0; else if (temp > MAXJSAMPLE) temp = MAXJSAMPLE; *elemptr++ = (JSAMPLE) temp; } } } } } } } LOCAL JSAMPIMAGE alloc_sampimage (decompress_info_ptr cinfo, int num_comps, long num_rows, long num_cols) /* Allocate an in-memory sample image (all components same size) */ { JSAMPIMAGE image; int ci; image = (JSAMPIMAGE) (*cinfo->emethods->alloc_small) (num_comps * SIZEOF(JSAMPARRAY)); for (ci = 0; ci < num_comps; ci++) { image[ci] = (*cinfo->emethods->alloc_small_sarray) (num_cols, num_rows); } return image; } LOCAL void free_sampimage (decompress_info_ptr cinfo, JSAMPIMAGE image, int num_comps, long num_rows) /* Release a sample image created by alloc_sampimage */ { int ci; for (ci = 0; ci < num_comps; ci++) { (*cinfo->emethods->free_small_sarray) (image[ci], num_rows); } (*cinfo->emethods->free_small) ((void *) image); } LOCAL JBLOCKIMAGE alloc_MCU_row (decompress_info_ptr cinfo) /* Allocate one MCU row's worth of coefficient blocks */ { JBLOCKIMAGE image; int ci; image = (JBLOCKIMAGE) (*cinfo->emethods->alloc_small) (cinfo->comps_in_scan * SIZEOF(JBLOCKARRAY)); for (ci = 0; ci < cinfo->comps_in_scan; ci++) { image[ci] = (*cinfo->emethods->alloc_small_barray) (cinfo->cur_comp_info[ci]->subsampled_width / DCTSIZE, (long) cinfo->cur_comp_info[ci]->MCU_height); } return image; } LOCAL void free_MCU_row (decompress_info_ptr cinfo, JBLOCKIMAGE image) /* Release a coefficient block array created by alloc_MCU_row */ { int ci; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { (*cinfo->emethods->free_small_barray) (image[ci], (long) cinfo->cur_comp_info[ci]->MCU_height); } (*cinfo->emethods->free_small) ((void *) image); } LOCAL void alloc_sampling_buffer (decompress_info_ptr cinfo, JSAMPIMAGE subsampled_data[2]) /* Create a subsampled-data buffer having the desired structure */ /* (see comments at head of file) */ { short ci, vs, i; /* Get top-level space for array pointers */ subsampled_data[0] = (JSAMPIMAGE) (*cinfo->emethods->alloc_small) (cinfo->comps_in_scan * SIZEOF(JSAMPARRAY)); subsampled_data[1] = (JSAMPIMAGE) (*cinfo->emethods->alloc_small) (cinfo->comps_in_scan * SIZEOF(JSAMPARRAY)); for (ci = 0; ci < cinfo->comps_in_scan; ci++) { vs = cinfo->cur_comp_info[ci]->v_samp_factor; /* row group height */ /* Allocate the real storage */ subsampled_data[0][ci] = (*cinfo->emethods->alloc_small_sarray) (cinfo->cur_comp_info[ci]->subsampled_width, (long) (vs * (DCTSIZE+2))); /* Create space for the scrambled-order pointers */ subsampled_data[1][ci] = (JSAMPARRAY) (*cinfo->emethods->alloc_small) (vs * (DCTSIZE+2) * SIZEOF(JSAMPROW)); /* Duplicate the first DCTSIZE-2 row groups */ for (i = 0; i < vs * (DCTSIZE-2); i++) { subsampled_data[1][ci][i] = subsampled_data[0][ci][i]; } /* Copy the last four row groups in swapped order */ for (i = 0; i < vs * 2; i++) { subsampled_data[1][ci][vs*DCTSIZE + i] = subsampled_data[0][ci][vs*(DCTSIZE-2) + i]; subsampled_data[1][ci][vs*(DCTSIZE-2) + i] = subsampled_data[0][ci][vs*DCTSIZE + i]; } } } LOCAL void free_sampling_buffer (decompress_info_ptr cinfo, JSAMPIMAGE subsampled_data[2]) /* Release a sampling buffer created by alloc_sampling_buffer */ { short ci, vs; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { vs = cinfo->cur_comp_info[ci]->v_samp_factor; /* row group height */ /* Free the real storage */ (*cinfo->emethods->free_small_sarray) (subsampled_data[0][ci], (long) (vs * (DCTSIZE+2))); /* Free the scrambled-order pointers */ (*cinfo->emethods->free_small) ((void *) subsampled_data[1][ci]); } /* Free the top-level space */ (*cinfo->emethods->free_small) ((void *) subsampled_data[0]); (*cinfo->emethods->free_small) ((void *) subsampled_data[1]); } LOCAL void duplicate_row (JSAMPARRAY image_data, long num_cols, int source_row, int num_rows) /* Duplicate the source_row at source_row+1 .. source_row+num_rows */ /* This happens only at the bottom of the image, */ /* so it needn't be super-efficient */ { register int row; for (row = 1; row <= num_rows; row++) { jcopy_sample_rows(image_data, source_row, image_data, source_row + row, 1, num_cols); } } LOCAL void expand (decompress_info_ptr cinfo, JSAMPIMAGE subsampled_data, JSAMPIMAGE fullsize_data, long fullsize_width, short above, short current, short below, short out) /* Do unsubsampling expansion of a single row group (of each component). */ /* above, current, below are indexes of row groups in subsampled_data; */ /* out is the index of the target row group in fullsize_data. */ /* Special case: above, below can be -1 to indicate top, bottom of image. */ { jpeg_component_info *compptr; JSAMPARRAY above_ptr, below_ptr; JSAMPROW dummy[MAX_SAMP_FACTOR]; /* for subsample expansion at top/bottom */ short ci, vs, i; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; vs = compptr->v_samp_factor; /* row group height */ if (above >= 0) above_ptr = subsampled_data[ci] + above * vs; else { /* Top of image: make a dummy above-context with copies of 1st row */ /* We assume current=0 in this case */ for (i = 0; i < vs; i++) dummy[i] = subsampled_data[ci][0]; above_ptr = (JSAMPARRAY) dummy; /* possible near->far pointer conv */ } if (below >= 0) below_ptr = subsampled_data[ci] + below * vs; else { /* Bot of image: make a dummy below-context with copies of last row */ for (i = 0; i < vs; i++) dummy[i] = subsampled_data[ci][(current+1)*vs-1]; below_ptr = (JSAMPARRAY) dummy; /* possible near->far pointer conv */ } (*cinfo->methods->unsubsample[ci]) (cinfo, (int) ci, compptr->subsampled_width, (int) vs, fullsize_width, (int) cinfo->max_v_samp_factor, above_ptr, subsampled_data[ci] + current * vs, below_ptr, fullsize_data[ci] + out * cinfo->max_v_samp_factor); } } LOCAL void emit_1pass (decompress_info_ptr cinfo, int num_rows, JSAMPIMAGE fullsize_data, JSAMPIMAGE color_data) /* Do color conversion and output of num_rows full-size rows. */ /* This is not used for 2-pass color quantization. */ { (*cinfo->methods->color_convert) (cinfo, num_rows, fullsize_data, color_data); if (cinfo->quantize_colors) { (*cinfo->methods->color_quantize) (cinfo, num_rows, color_data, quantize_out); (*cinfo->methods->put_pixel_rows) (cinfo, num_rows, &quantize_out); } else { (*cinfo->methods->put_pixel_rows) (cinfo, num_rows, color_data); } } /* * Support routines for 2-pass color quantization. */ #ifdef QUANT_2PASS_SUPPORTED LOCAL void emit_2pass (decompress_info_ptr cinfo, long top_row, int num_rows, JSAMPIMAGE fullsize_data) /* Do color conversion and output data to the quantization buffer image. */ /* This is used only with 2-pass color quantization. */ { short ci; /* Realign the big buffers */ for (ci = 0; ci < cinfo->num_components; ci++) { fullsize_cnvt_ptrs[ci] = (*cinfo->emethods->access_big_sarray) (fullsize_cnvt_image[ci], top_row, TRUE); } /* Do colorspace conversion */ (*cinfo->methods->color_convert) (cinfo, num_rows, fullsize_data, fullsize_cnvt_ptrs); /* Let quantizer get first-pass peek at the data. */ /* (Quantizer could change data if it wants to.) */ (*cinfo->methods->color_quant_prescan) (cinfo, num_rows, fullsize_cnvt_ptrs); } METHODDEF void scan_big_image (decompress_info_ptr cinfo, quantize_method_ptr quantize_method) /* This is the "iterator" routine used by the quantizer. */ { long pixel_rows_output; short ci; for (pixel_rows_output = 0; pixel_rows_output < cinfo->image_height; pixel_rows_output += rows_in_mem) { /* Realign the big buffers */ for (ci = 0; ci < cinfo->num_components; ci++) { fullsize_cnvt_ptrs[ci] = (*cinfo->emethods->access_big_sarray) (fullsize_cnvt_image[ci], pixel_rows_output, FALSE); } /* Let the quantizer have its way with the data. * Note that quantize_out is simply workspace for the quantizer; * when it's ready to output, it must call put_pixel_rows itself. */ (*quantize_method) (cinfo, (int) MIN(rows_in_mem, cinfo->image_height - pixel_rows_output), fullsize_cnvt_ptrs, quantize_out); } } #endif /* QUANT_2PASS_SUPPORTED */ /* * Support routines for cross-block smoothing. */ #ifdef BLOCK_SMOOTHING_SUPPORTED LOCAL void smooth_mcu_row (decompress_info_ptr cinfo, JBLOCKIMAGE above, JBLOCKIMAGE input, JBLOCKIMAGE below, JBLOCKIMAGE output) /* Apply cross-block smoothing to one MCU row's worth of coefficient blocks. */ /* above,below are NULL if at top/bottom of image. */ { jpeg_component_info *compptr; short ci, ri, last; JBLOCKROW prev; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; last = compptr->MCU_height - 1; if (above == NULL) prev = NULL; else prev = above[ci][last]; for (ri = 0; ri < last; ri++) { (*cinfo->methods->smooth_coefficients) (cinfo, compptr, prev, input[ci][ri], input[ci][ri+1], output[ci][ri]); prev = input[ci][ri]; } if (below == NULL) (*cinfo->methods->smooth_coefficients) (cinfo, compptr, prev, input[ci][last], (JBLOCKROW) NULL, output[ci][last]); else (*cinfo->methods->smooth_coefficients) (cinfo, compptr, prev, input[ci][last], below[ci][0], output[ci][last]); } } LOCAL void get_smoothed_row (decompress_info_ptr cinfo, JBLOCKIMAGE coeff_data, JBLOCKIMAGE bsmooth[3], int * whichb, long cur_mcu_row) /* Get an MCU row of coefficients, applying cross-block smoothing. */ /* The output row is placed in coeff_data. bsmooth and whichb hold */ /* working state, and cur_row is needed to check for image top/bottom. */ /* This routine just takes care of the buffering logic. */ { int prev, cur, next; /* Special case for top of image: need to pre-fetch a row & init whichb */ if (cur_mcu_row == 0) { (*cinfo->methods->disassemble_MCU) (cinfo, bsmooth[0]); if (cinfo->MCU_rows_in_scan > 1) { (*cinfo->methods->disassemble_MCU) (cinfo, bsmooth[1]); smooth_mcu_row(cinfo, (JBLOCKIMAGE) NULL, bsmooth[0], bsmooth[1], coeff_data); } else { smooth_mcu_row(cinfo, (JBLOCKIMAGE) NULL, bsmooth[0], (JBLOCKIMAGE) NULL, coeff_data); } *whichb = 1; /* points to next bsmooth[] element to use */ return; } cur = *whichb; /* set up references */ prev = (cur == 0 ? 2 : cur - 1); next = (cur == 2 ? 0 : cur + 1); *whichb = next; /* advance whichb for next time */ /* Special case for bottom of image: don't read another row */ if (cur_mcu_row >= cinfo->MCU_rows_in_scan - 1) { smooth_mcu_row(cinfo, bsmooth[prev], bsmooth[cur], (JBLOCKIMAGE) NULL, coeff_data); return; } /* Normal case: read ahead a new row, smooth the one I got before */ (*cinfo->methods->disassemble_MCU) (cinfo, bsmooth[next]); smooth_mcu_row(cinfo, bsmooth[prev], bsmooth[cur], bsmooth[next], coeff_data); } #endif /* BLOCK_SMOOTHING_SUPPORTED */ /* * Decompression pipeline controller used for single-scan files * without 2-pass color quantization. */ METHODDEF void single_dcontroller (decompress_info_ptr cinfo) { long fullsize_width; /* # of samples per row in full-size buffers */ long cur_mcu_row; /* counts # of MCU rows processed */ long pixel_rows_output; /* # of pixel rows actually emitted */ int mcu_rows_per_loop; /* # of MCU rows processed per outer loop */ /* Work buffer for dequantized coefficients (IDCT input) */ JBLOCKIMAGE coeff_data; /* Work buffer for cross-block smoothing input */ #ifdef BLOCK_SMOOTHING_SUPPORTED JBLOCKIMAGE bsmooth[3]; /* this is optional */ int whichb; #endif /* Work buffer for subsampled image data (see comments at head of file) */ JSAMPIMAGE subsampled_data[2]; /* Work buffer for desubsampled data */ JSAMPIMAGE fullsize_data; /* Work buffer for color conversion output (full size) */ JSAMPIMAGE color_data; int whichss, ri; short i; /* Initialize for 1-pass color quantization, if needed */ if (cinfo->quantize_colors) (*cinfo->methods->color_quant_init) (cinfo); /* Prepare for single scan containing all components */ if (cinfo->comps_in_scan == 1) { noninterleaved_scan_setup(cinfo); /* Need to read Vk MCU rows to obtain Vk block rows */ mcu_rows_per_loop = cinfo->cur_comp_info[0]->v_samp_factor; } else { interleaved_scan_setup(cinfo); /* in an interleaved scan, one MCU row provides Vk block rows */ mcu_rows_per_loop = 1; } /* Compute dimensions of full-size pixel buffers */ /* Note these are the same whether interleaved or not. */ rows_in_mem = cinfo->max_v_samp_factor * DCTSIZE; fullsize_width = jround_up(cinfo->image_width, (long) (cinfo->max_h_samp_factor * DCTSIZE)); /* Allocate working memory: */ /* coeff_data holds a single MCU row of coefficient blocks */ coeff_data = alloc_MCU_row(cinfo); /* if doing cross-block smoothing, need extra space for its input */ #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) { bsmooth[0] = alloc_MCU_row(cinfo); bsmooth[1] = alloc_MCU_row(cinfo); bsmooth[2] = alloc_MCU_row(cinfo); } #endif /* subsampled_data is sample data before unsubsampling */ alloc_sampling_buffer(cinfo, subsampled_data); /* fullsize_data is sample data after unsubsampling */ fullsize_data = alloc_sampimage(cinfo, (int) cinfo->num_components, (long) rows_in_mem, fullsize_width); /* color_data is the result of the colorspace conversion step */ color_data = alloc_sampimage(cinfo, (int) cinfo->color_out_comps, (long) rows_in_mem, fullsize_width); /* if quantizing colors, also need a one-component output area for that. */ if (cinfo->quantize_colors) quantize_out = (*cinfo->emethods->alloc_small_sarray) (fullsize_width, (long) rows_in_mem); /* Tell the memory manager to instantiate big arrays. * We don't need any big arrays in this controller, * but some other module (like the output file writer) may need one. */ (*cinfo->emethods->alloc_big_arrays) ((long) 0, /* no more small sarrays */ (long) 0, /* no more small barrays */ (long) 0); /* no more "medium" objects */ /* NB: quantizer must get any such objects at color_quant_init time */ /* Initialize to read scan data */ (*cinfo->methods->entropy_decoder_init) (cinfo); (*cinfo->methods->unsubsample_init) (cinfo); (*cinfo->methods->disassemble_init) (cinfo); /* Loop over scan's data: rows_in_mem pixel rows are processed per loop */ pixel_rows_output = 0; whichss = 1; /* arrange to start with subsampled_data[0] */ for (cur_mcu_row = 0; cur_mcu_row < cinfo->MCU_rows_in_scan; cur_mcu_row += mcu_rows_per_loop) { whichss ^= 1; /* switch to other subsample buffer */ /* Obtain v_samp_factor block rows of each component in the scan. */ /* This is a single MCU row if interleaved, multiple MCU rows if not. */ /* In the noninterleaved case there might be fewer than v_samp_factor */ /* block rows remaining; if so, pad with copies of the last pixel row */ /* so that unsubsampling doesn't have to treat it as a special case. */ for (ri = 0; ri < mcu_rows_per_loop; ri++) { if (cur_mcu_row + ri < cinfo->MCU_rows_in_scan) { /* OK to actually read an MCU row. */ #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) get_smoothed_row(cinfo, coeff_data, bsmooth, &whichb, cur_mcu_row + ri); else #endif (*cinfo->methods->disassemble_MCU) (cinfo, coeff_data); reverse_DCT(cinfo, coeff_data, subsampled_data[whichss], ri * DCTSIZE); } else { /* Need to pad out with copies of the last subsampled row. */ /* This can only happen if there is just one component. */ duplicate_row(subsampled_data[whichss][0], cinfo->cur_comp_info[0]->subsampled_width, ri * DCTSIZE - 1, DCTSIZE); } } /* Unsubsample the data */ /* First time through is a special case */ if (cur_mcu_row) { /* Expand last row group of previous set */ expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) DCTSIZE, (short) (DCTSIZE+1), (short) 0, (short) (DCTSIZE-1)); /* and dump the previous set's expanded data */ emit_1pass (cinfo, rows_in_mem, fullsize_data, color_data); pixel_rows_output += rows_in_mem; /* Expand first row group of this set */ expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) (DCTSIZE+1), (short) 0, (short) 1, (short) 0); } else { /* Expand first row group with dummy above-context */ expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) (-1), (short) 0, (short) 1, (short) 0); } /* Expand second through next-to-last row groups of this set */ for (i = 1; i <= DCTSIZE-2; i++) { expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) (i-1), (short) i, (short) (i+1), (short) i); } } /* end of outer loop */ /* Expand the last row group with dummy below-context */ /* Note whichss points to last buffer side used */ expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) (DCTSIZE-2), (short) (DCTSIZE-1), (short) (-1), (short) (DCTSIZE-1)); /* and dump the remaining data (may be less than full height) */ emit_1pass (cinfo, (int) (cinfo->image_height - pixel_rows_output), fullsize_data, color_data); /* Clean up after the scan */ (*cinfo->methods->disassemble_term) (cinfo); (*cinfo->methods->unsubsample_term) (cinfo); (*cinfo->methods->entropy_decoder_term) (cinfo); (*cinfo->methods->read_scan_trailer) (cinfo); /* Verify that we've seen the whole input file */ if ((*cinfo->methods->read_scan_header) (cinfo)) ERREXIT(cinfo->emethods, "Didn't expect more than one scan"); /* Release working memory */ free_MCU_row(cinfo, coeff_data); #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) { free_MCU_row(cinfo, bsmooth[0]); free_MCU_row(cinfo, bsmooth[1]); free_MCU_row(cinfo, bsmooth[2]); } #endif free_sampling_buffer(cinfo, subsampled_data); free_sampimage(cinfo, fullsize_data, (int) cinfo->num_components, (long) rows_in_mem); free_sampimage(cinfo, color_data, (int) cinfo->color_out_comps, (long) rows_in_mem); if (cinfo->quantize_colors) (*cinfo->emethods->free_small_sarray) (quantize_out, (long) rows_in_mem); /* Close up shop */ if (cinfo->quantize_colors) (*cinfo->methods->color_quant_term) (cinfo); } /* * Decompression pipeline controller used for single-scan files * with 2-pass color quantization. */ #ifdef QUANT_2PASS_SUPPORTED METHODDEF void single_2quant_dcontroller (decompress_info_ptr cinfo) { long fullsize_width; /* # of samples per row in full-size buffers */ long cur_mcu_row; /* counts # of MCU rows processed */ long pixel_rows_output; /* # of pixel rows actually emitted */ int mcu_rows_per_loop; /* # of MCU rows processed per outer loop */ /* Work buffer for dequantized coefficients (IDCT input) */ JBLOCKIMAGE coeff_data; /* Work buffer for cross-block smoothing input */ #ifdef BLOCK_SMOOTHING_SUPPORTED JBLOCKIMAGE bsmooth[3]; /* this is optional */ int whichb; #endif /* Work buffer for subsampled image data (see comments at head of file) */ JSAMPIMAGE subsampled_data[2]; /* Work buffer for desubsampled data */ JSAMPIMAGE fullsize_data; int whichss, ri; short ci, i; /* Initialize for 2-pass color quantization */ (*cinfo->methods->color_quant_init) (cinfo); /* Prepare for single scan containing all components */ if (cinfo->comps_in_scan == 1) { noninterleaved_scan_setup(cinfo); /* Need to read Vk MCU rows to obtain Vk block rows */ mcu_rows_per_loop = cinfo->cur_comp_info[0]->v_samp_factor; } else { interleaved_scan_setup(cinfo); /* in an interleaved scan, one MCU row provides Vk block rows */ mcu_rows_per_loop = 1; } /* Compute dimensions of full-size pixel buffers */ /* Note these are the same whether interleaved or not. */ rows_in_mem = cinfo->max_v_samp_factor * DCTSIZE; fullsize_width = jround_up(cinfo->image_width, (long) (cinfo->max_h_samp_factor * DCTSIZE)); /* Allocate working memory: */ /* coeff_data holds a single MCU row of coefficient blocks */ coeff_data = alloc_MCU_row(cinfo); /* if doing cross-block smoothing, need extra space for its input */ #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) { bsmooth[0] = alloc_MCU_row(cinfo); bsmooth[1] = alloc_MCU_row(cinfo); bsmooth[2] = alloc_MCU_row(cinfo); } #endif /* subsampled_data is sample data before unsubsampling */ alloc_sampling_buffer(cinfo, subsampled_data); /* fullsize_data is sample data after unsubsampling */ fullsize_data = alloc_sampimage(cinfo, (int) cinfo->num_components, (long) rows_in_mem, fullsize_width); /* Also need a one-component output area for color quantizer. */ quantize_out = (*cinfo->emethods->alloc_small_sarray) (fullsize_width, (long) rows_in_mem); /* Get a big image for quantizer input: desubsampled, color-converted data */ fullsize_cnvt_image = (big_sarray_ptr *) (*cinfo->emethods->alloc_small) (cinfo->num_components * SIZEOF(big_sarray_ptr)); for (ci = 0; ci < cinfo->num_components; ci++) { fullsize_cnvt_image[ci] = (*cinfo->emethods->request_big_sarray) (fullsize_width, jround_up(cinfo->image_height, (long) rows_in_mem), (long) rows_in_mem); } /* Also get an area for pointers to currently accessible chunks */ fullsize_cnvt_ptrs = (JSAMPIMAGE) (*cinfo->emethods->alloc_small) (cinfo->num_components * SIZEOF(JSAMPARRAY)); /* Tell the memory manager to instantiate big arrays */ (*cinfo->emethods->alloc_big_arrays) ((long) 0, /* no more small sarrays */ (long) 0, /* no more small barrays */ (long) 0); /* no more "medium" objects */ /* NB: quantizer must get any such objects at color_quant_init time */ /* Initialize to read scan data */ (*cinfo->methods->entropy_decoder_init) (cinfo); (*cinfo->methods->unsubsample_init) (cinfo); (*cinfo->methods->disassemble_init) (cinfo); /* Loop over scan's data: rows_in_mem pixel rows are processed per loop */ pixel_rows_output = 0; whichss = 1; /* arrange to start with subsampled_data[0] */ for (cur_mcu_row = 0; cur_mcu_row < cinfo->MCU_rows_in_scan; cur_mcu_row += mcu_rows_per_loop) { whichss ^= 1; /* switch to other subsample buffer */ /* Obtain v_samp_factor block rows of each component in the scan. */ /* This is a single MCU row if interleaved, multiple MCU rows if not. */ /* In the noninterleaved case there might be fewer than v_samp_factor */ /* block rows remaining; if so, pad with copies of the last pixel row */ /* so that unsubsampling doesn't have to treat it as a special case. */ for (ri = 0; ri < mcu_rows_per_loop; ri++) { if (cur_mcu_row + ri < cinfo->MCU_rows_in_scan) { /* OK to actually read an MCU row. */ #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) get_smoothed_row(cinfo, coeff_data, bsmooth, &whichb, cur_mcu_row + ri); else #endif (*cinfo->methods->disassemble_MCU) (cinfo, coeff_data); reverse_DCT(cinfo, coeff_data, subsampled_data[whichss], ri * DCTSIZE); } else { /* Need to pad out with copies of the last subsampled row. */ /* This can only happen if there is just one component. */ duplicate_row(subsampled_data[whichss][0], cinfo->cur_comp_info[0]->subsampled_width, ri * DCTSIZE - 1, DCTSIZE); } } /* Unsubsample the data */ /* First time through is a special case */ if (cur_mcu_row) { /* Expand last row group of previous set */ expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) DCTSIZE, (short) (DCTSIZE+1), (short) 0, (short) (DCTSIZE-1)); /* and dump the previous set's expanded data */ emit_2pass (cinfo, pixel_rows_output, rows_in_mem, fullsize_data); pixel_rows_output += rows_in_mem; /* Expand first row group of this set */ expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) (DCTSIZE+1), (short) 0, (short) 1, (short) 0); } else { /* Expand first row group with dummy above-context */ expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) (-1), (short) 0, (short) 1, (short) 0); } /* Expand second through next-to-last row groups of this set */ for (i = 1; i <= DCTSIZE-2; i++) { expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) (i-1), (short) i, (short) (i+1), (short) i); } } /* end of outer loop */ /* Expand the last row group with dummy below-context */ /* Note whichss points to last buffer side used */ expand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width, (short) (DCTSIZE-2), (short) (DCTSIZE-1), (short) (-1), (short) (DCTSIZE-1)); /* and dump the remaining data (may be less than full height) */ emit_2pass (cinfo, pixel_rows_output, (int) (cinfo->image_height - pixel_rows_output), fullsize_data); /* Clean up after the scan */ (*cinfo->methods->disassemble_term) (cinfo); (*cinfo->methods->unsubsample_term) (cinfo); (*cinfo->methods->entropy_decoder_term) (cinfo); (*cinfo->methods->read_scan_trailer) (cinfo); /* Verify that we've seen the whole input file */ if ((*cinfo->methods->read_scan_header) (cinfo)) ERREXIT(cinfo->emethods, "Didn't expect more than one scan"); /* Now that we've collected the data, let the color quantizer do its thing */ (*cinfo->methods->color_quant_doit) (cinfo, scan_big_image); /* Release working memory */ free_MCU_row(cinfo, coeff_data); #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) { free_MCU_row(cinfo, bsmooth[0]); free_MCU_row(cinfo, bsmooth[1]); free_MCU_row(cinfo, bsmooth[2]); } #endif free_sampling_buffer(cinfo, subsampled_data); free_sampimage(cinfo, fullsize_data, (int) cinfo->num_components, (long) rows_in_mem); (*cinfo->emethods->free_small_sarray) (quantize_out, (long) rows_in_mem); for (ci = 0; ci < cinfo->num_components; ci++) { (*cinfo->emethods->free_big_sarray) (fullsize_cnvt_image[ci]); } (*cinfo->emethods->free_small) ((void *) fullsize_cnvt_image); (*cinfo->emethods->free_small) ((void *) fullsize_cnvt_ptrs); /* Close up shop */ (*cinfo->methods->color_quant_term) (cinfo); } #endif /* QUANT_2PASS_SUPPORTED */ /* * Decompression pipeline controller used for multiple-scan files * without 2-pass color quantization. * * The current implementation places the "big" buffer at the stage of * desubsampled data. Buffering subsampled data instead would reduce the * size of temp files (by about a factor of 2 in typical cases). However, * the unsubsampling logic is dependent on the assumption that unsubsampling * occurs during a scan, so it's much easier to do the enlargement as the * JPEG file is read. This also simplifies life for the memory manager, * which would otherwise have to deal with overlapping access_big_sarray() * requests. * * At present it appears that most JPEG files will be single-scan, so * it doesn't seem worthwhile to try to make this implementation smarter. */ #ifdef MULTISCAN_FILES_SUPPORTED METHODDEF void multi_dcontroller (decompress_info_ptr cinfo) { long fullsize_width; /* # of samples per row in full-size buffers */ long cur_mcu_row; /* counts # of MCU rows processed */ long pixel_rows_output; /* # of pixel rows actually emitted */ int mcu_rows_per_loop; /* # of MCU rows processed per outer loop */ /* Work buffer for dequantized coefficients (IDCT input) */ JBLOCKIMAGE coeff_data; /* Work buffer for cross-block smoothing input */ #ifdef BLOCK_SMOOTHING_SUPPORTED JBLOCKIMAGE bsmooth[3]; /* this is optional */ int whichb; #endif /* Work buffer for subsampled image data (see comments at head of file) */ JSAMPIMAGE subsampled_data[2]; /* Full-image buffer holding desubsampled, but not color-converted, data */ big_sarray_ptr *fullsize_image; JSAMPIMAGE fullsize_ptrs; /* workspace for access_big_sarray() results */ /* Work buffer for color conversion output (full size) */ JSAMPIMAGE color_data; int whichss, ri; short ci, i; /* Initialize for 1-pass color quantization, if needed */ if (cinfo->quantize_colors) (*cinfo->methods->color_quant_init) (cinfo); /* Compute dimensions of full-size pixel buffers */ /* Note these are the same whether interleaved or not. */ rows_in_mem = cinfo->max_v_samp_factor * DCTSIZE; fullsize_width = jround_up(cinfo->image_width, (long) (cinfo->max_h_samp_factor * DCTSIZE)); /* Allocate all working memory that doesn't depend on scan info */ /* color_data is the result of the colorspace conversion step */ color_data = alloc_sampimage(cinfo, (int) cinfo->color_out_comps, (long) rows_in_mem, fullsize_width); /* if quantizing colors, also need a one-component output area for that. */ if (cinfo->quantize_colors) quantize_out = (*cinfo->emethods->alloc_small_sarray) (fullsize_width, (long) rows_in_mem); /* Get a big image: fullsize_image is sample data after unsubsampling. */ fullsize_image = (big_sarray_ptr *) (*cinfo->emethods->alloc_small) (cinfo->num_components * SIZEOF(big_sarray_ptr)); for (ci = 0; ci < cinfo->num_components; ci++) { fullsize_image[ci] = (*cinfo->emethods->request_big_sarray) (fullsize_width, jround_up(cinfo->image_height, (long) rows_in_mem), (long) rows_in_mem); } /* Also get an area for pointers to currently accessible chunks */ fullsize_ptrs = (JSAMPIMAGE) (*cinfo->emethods->alloc_small) (cinfo->num_components * SIZEOF(JSAMPARRAY)); /* Tell the memory manager to instantiate big arrays */ (*cinfo->emethods->alloc_big_arrays) /* extra sarray space is for subsampled-data buffers: */ ((long) (fullsize_width /* max width in samples */ * cinfo->max_v_samp_factor*(DCTSIZE+2) /* max height */ * cinfo->num_components), /* max components per scan */ /* extra barray space is for MCU-row buffers: */ (long) ((fullsize_width / DCTSIZE) /* max width in blocks */ * cinfo->max_v_samp_factor /* max height */ * cinfo->num_components /* max components per scan */ * (cinfo->do_block_smoothing ? 4 : 1)),/* how many of these we need */ /* no extra "medium"-object space */ /* NB: quantizer must get any such objects at color_quant_init time */ (long) 0); /* Loop over scans in file */ do { /* Prepare for this scan */ if (cinfo->comps_in_scan == 1) { noninterleaved_scan_setup(cinfo); /* Need to read Vk MCU rows to obtain Vk block rows */ mcu_rows_per_loop = cinfo->cur_comp_info[0]->v_samp_factor; } else { interleaved_scan_setup(cinfo); /* in an interleaved scan, one MCU row provides Vk block rows */ mcu_rows_per_loop = 1; } /* Allocate scan-local working memory */ /* coeff_data holds a single MCU row of coefficient blocks */ coeff_data = alloc_MCU_row(cinfo); /* if doing cross-block smoothing, need extra space for its input */ #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) { bsmooth[0] = alloc_MCU_row(cinfo); bsmooth[1] = alloc_MCU_row(cinfo); bsmooth[2] = alloc_MCU_row(cinfo); } #endif /* subsampled_data is sample data before unsubsampling */ alloc_sampling_buffer(cinfo, subsampled_data); /* line up the big buffers */ for (ci = 0; ci < cinfo->comps_in_scan; ci++) { fullsize_ptrs[ci] = (*cinfo->emethods->access_big_sarray) (fullsize_image[cinfo->cur_comp_info[ci]->component_index], (long) 0, TRUE); } /* Initialize to read scan data */ (*cinfo->methods->entropy_decoder_init) (cinfo); (*cinfo->methods->unsubsample_init) (cinfo); (*cinfo->methods->disassemble_init) (cinfo); /* Loop over scan's data: rows_in_mem pixel rows are processed per loop */ pixel_rows_output = 0; whichss = 1; /* arrange to start with subsampled_data[0] */ for (cur_mcu_row = 0; cur_mcu_row < cinfo->MCU_rows_in_scan; cur_mcu_row += mcu_rows_per_loop) { whichss ^= 1; /* switch to other subsample buffer */ /* Obtain v_samp_factor block rows of each component in the scan. */ /* This is a single MCU row if interleaved, multiple MCU rows if not. */ /* In the noninterleaved case there might be fewer than v_samp_factor */ /* block rows remaining; if so, pad with copies of the last pixel row */ /* so that unsubsampling doesn't have to treat it as a special case. */ for (ri = 0; ri < mcu_rows_per_loop; ri++) { if (cur_mcu_row + ri < cinfo->MCU_rows_in_scan) { /* OK to actually read an MCU row. */ #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) get_smoothed_row(cinfo, coeff_data, bsmooth, &whichb, cur_mcu_row + ri); else #endif (*cinfo->methods->disassemble_MCU) (cinfo, coeff_data); reverse_DCT(cinfo, coeff_data, subsampled_data[whichss], ri * DCTSIZE); } else { /* Need to pad out with copies of the last subsampled row. */ /* This can only happen if there is just one component. */ duplicate_row(subsampled_data[whichss][0], cinfo->cur_comp_info[0]->subsampled_width, ri * DCTSIZE - 1, DCTSIZE); } } /* Unsubsample the data */ /* First time through is a special case */ if (cur_mcu_row) { /* Expand last row group of previous set */ expand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width, (short) DCTSIZE, (short) (DCTSIZE+1), (short) 0, (short) (DCTSIZE-1)); /* Realign the big buffers */ pixel_rows_output += rows_in_mem; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { fullsize_ptrs[ci] = (*cinfo->emethods->access_big_sarray) (fullsize_image[cinfo->cur_comp_info[ci]->component_index], pixel_rows_output, TRUE); } /* Expand first row group of this set */ expand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width, (short) (DCTSIZE+1), (short) 0, (short) 1, (short) 0); } else { /* Expand first row group with dummy above-context */ expand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width, (short) (-1), (short) 0, (short) 1, (short) 0); } /* Expand second through next-to-last row groups of this set */ for (i = 1; i <= DCTSIZE-2; i++) { expand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width, (short) (i-1), (short) i, (short) (i+1), (short) i); } } /* end of outer loop */ /* Expand the last row group with dummy below-context */ /* Note whichss points to last buffer side used */ expand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width, (short) (DCTSIZE-2), (short) (DCTSIZE-1), (short) (-1), (short) (DCTSIZE-1)); /* Clean up after the scan */ (*cinfo->methods->disassemble_term) (cinfo); (*cinfo->methods->unsubsample_term) (cinfo); (*cinfo->methods->entropy_decoder_term) (cinfo); (*cinfo->methods->read_scan_trailer) (cinfo); /* Release scan-local working memory */ free_MCU_row(cinfo, coeff_data); #ifdef BLOCK_SMOOTHING_SUPPORTED if (cinfo->do_block_smoothing) { free_MCU_row(cinfo, bsmooth[0]); free_MCU_row(cinfo, bsmooth[1]); free_MCU_row(cinfo, bsmooth[2]); } #endif free_sampling_buffer(cinfo, subsampled_data); /* Repeat if there is another scan */ } while ((*cinfo->methods->read_scan_header) (cinfo)); /* Now that we've collected all the data, color convert & output it. */ for (pixel_rows_output = 0; pixel_rows_output < cinfo->image_height; pixel_rows_output += rows_in_mem) { /* realign the big buffers */ for (ci = 0; ci < cinfo->num_components; ci++) { fullsize_ptrs[ci] = (*cinfo->emethods->access_big_sarray) (fullsize_image[ci], pixel_rows_output, FALSE); } emit_1pass (cinfo, (int) MIN((long) rows_in_mem, cinfo->image_height - pixel_rows_output), fullsize_ptrs, color_data); } /* Release working memory */ free_sampimage(cinfo, color_data, (int) cinfo->color_out_comps, (long) rows_in_mem); if (cinfo->quantize_colors) (*cinfo->emethods->free_small_sarray) (quantize_out, (long) rows_in_mem); for (ci = 0; ci < cinfo->num_components; ci++) { (*cinfo->emethods->free_big_sarray) (fullsize_image[ci]); } (*cinfo->emethods->free_small) ((void *) fullsize_image); (*cinfo->emethods->free_small) ((void *) fullsize_ptrs); /* Close up shop */ if (cinfo->quantize_colors) (*cinfo->methods->color_quant_term) (cinfo); } #endif /* MULTISCAN_FILES_SUPPORTED */ /* * Decompression pipeline controller used for multiple-scan files * with 2-pass color quantization. */ #ifdef MULTISCAN_FILES_SUPPORTED #ifdef QUANT_2PASS_SUPPORTED METHODDEF void multi_2quant_dcontroller (decompress_info_ptr cinfo) { ERREXIT(cinfo->emethods, "Not implemented yet"); } #endif /* QUANT_2PASS_SUPPORTED */ #endif /* MULTISCAN_FILES_SUPPORTED */ /* * The method selection routine for decompression pipeline controllers. * Note that at this point we've already read the JPEG header and first SOS, * so we can tell whether the input is one scan or not. */ GLOBAL void jseldpipeline (decompress_info_ptr cinfo) { /* simplify subsequent tests on color quantization */ if (! cinfo->quantize_colors) cinfo->two_pass_quantize = FALSE; if (cinfo->comps_in_scan == cinfo->num_components) { /* It's a single-scan file */ #ifdef QUANT_2PASS_SUPPORTED if (cinfo->two_pass_quantize) cinfo->methods->d_pipeline_controller = single_2quant_dcontroller; else #endif cinfo->methods->d_pipeline_controller = single_dcontroller; } else { /* It's a multiple-scan file */ #ifdef MULTISCAN_FILES_SUPPORTED #ifdef QUANT_2PASS_SUPPORTED if (cinfo->two_pass_quantize) cinfo->methods->d_pipeline_controller = multi_2quant_dcontroller; else #endif cinfo->methods->d_pipeline_controller = multi_dcontroller; #else ERREXIT(cinfo->emethods, "Multiple-scan support was not compiled"); #endif } }