зеркало из https://github.com/mozilla/mozjpeg.git
718 строки
26 KiB
C
718 строки
26 KiB
C
/*
|
|
* jcpipe.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 compression pipeline controllers.
|
|
* These routines are invoked via the c_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).
|
|
*
|
|
* optimization of entropy encoding parameters
|
|
* vs. usage of default encoding parameters.
|
|
*
|
|
* Note that these conditions determine the needs for "big" arrays:
|
|
* multiple scans imply a big array for splitting the color components;
|
|
* entropy encoding optimization needs a big array for the MCU data.
|
|
*
|
|
* All but the simplest controller (single-scan, no optimization) can be
|
|
* compiled out through configuration options, if you need to make a minimal
|
|
* implementation.
|
|
*/
|
|
|
|
#include "jinclude.h"
|
|
|
|
|
|
/*
|
|
* About the data structures:
|
|
*
|
|
* The processing chunk size for subsampling 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 after subsampling, 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;
|
|
* for convenience we use a buffer of the same size as in interleaved scans,
|
|
* and process Vk MCU rows in each burst of subsampling.
|
|
* To provide context for the subsampling 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.
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
* Utility routines: common code for pipeline controllers
|
|
*/
|
|
|
|
LOCAL void
|
|
interleaved_scan_setup (compress_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->c_per_scan_method_selection) (cinfo);
|
|
}
|
|
|
|
|
|
LOCAL void
|
|
noninterleaved_scan_setup (compress_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->c_per_scan_method_selection) (cinfo);
|
|
}
|
|
|
|
|
|
|
|
LOCAL void
|
|
alloc_sampling_buffer (compress_info_ptr cinfo, JSAMPIMAGE fullsize_data[2],
|
|
long fullsize_width)
|
|
/* Create a pre-subsampling data buffer having the desired structure */
|
|
/* (see comments at head of file) */
|
|
{
|
|
short ci, vs, i;
|
|
|
|
vs = cinfo->max_v_samp_factor; /* row group height */
|
|
|
|
/* Get top-level space for array pointers */
|
|
fullsize_data[0] = (JSAMPIMAGE) (*cinfo->emethods->alloc_small)
|
|
(cinfo->num_components * SIZEOF(JSAMPARRAY));
|
|
fullsize_data[1] = (JSAMPIMAGE) (*cinfo->emethods->alloc_small)
|
|
(cinfo->num_components * SIZEOF(JSAMPARRAY));
|
|
|
|
for (ci = 0; ci < cinfo->num_components; ci++) {
|
|
/* Allocate the real storage */
|
|
fullsize_data[0][ci] = (*cinfo->emethods->alloc_small_sarray)
|
|
(fullsize_width,
|
|
(long) (vs * (DCTSIZE+2)));
|
|
/* Create space for the scrambled-order pointers */
|
|
fullsize_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++) {
|
|
fullsize_data[1][ci][i] = fullsize_data[0][ci][i];
|
|
}
|
|
/* Copy the last four row groups in swapped order */
|
|
for (i = 0; i < vs * 2; i++) {
|
|
fullsize_data[1][ci][vs*DCTSIZE + i] = fullsize_data[0][ci][vs*(DCTSIZE-2) + i];
|
|
fullsize_data[1][ci][vs*(DCTSIZE-2) + i] = fullsize_data[0][ci][vs*DCTSIZE + i];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LOCAL void
|
|
free_sampling_buffer (compress_info_ptr cinfo, JSAMPIMAGE fullsize_data[2])
|
|
/* Release a sampling buffer created by alloc_sampling_buffer */
|
|
{
|
|
short ci, vs;
|
|
|
|
vs = cinfo->max_v_samp_factor; /* row group height */
|
|
|
|
for (ci = 0; ci < cinfo->num_components; ci++) {
|
|
/* Free the real storage */
|
|
(*cinfo->emethods->free_small_sarray)
|
|
(fullsize_data[0][ci], (long) (vs * (DCTSIZE+2)));
|
|
/* Free the scrambled-order pointers */
|
|
(*cinfo->emethods->free_small) ((void *) fullsize_data[1][ci]);
|
|
}
|
|
|
|
/* Free the top-level space */
|
|
(*cinfo->emethods->free_small) ((void *) fullsize_data[0]);
|
|
(*cinfo->emethods->free_small) ((void *) fullsize_data[1]);
|
|
}
|
|
|
|
|
|
LOCAL void
|
|
subsample (compress_info_ptr cinfo,
|
|
JSAMPIMAGE fullsize_data, JSAMPIMAGE subsampled_data,
|
|
long fullsize_width,
|
|
short above, short current, short below, short out)
|
|
/* Do subsampling of a single row group (of each component). */
|
|
/* above, current, below are indexes of row groups in fullsize_data; */
|
|
/* out is the index of the target row group in subsampled_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;
|
|
|
|
vs = cinfo->max_v_samp_factor; /* row group height */
|
|
|
|
for (ci = 0; ci < cinfo->num_components; ci++) {
|
|
compptr = & cinfo->comp_info[ci];
|
|
|
|
if (above >= 0)
|
|
above_ptr = fullsize_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] = fullsize_data[ci][0];
|
|
above_ptr = (JSAMPARRAY) dummy; /* possible near->far pointer conv */
|
|
}
|
|
|
|
if (below >= 0)
|
|
below_ptr = fullsize_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] = fullsize_data[ci][(current+1)*vs-1];
|
|
below_ptr = (JSAMPARRAY) dummy; /* possible near->far pointer conv */
|
|
}
|
|
|
|
(*cinfo->methods->subsample[ci])
|
|
(cinfo, (int) ci,
|
|
fullsize_width, (int) vs,
|
|
compptr->subsampled_width, (int) compptr->v_samp_factor,
|
|
above_ptr,
|
|
fullsize_data[ci] + current * vs,
|
|
below_ptr,
|
|
subsampled_data[ci] + out * compptr->v_samp_factor);
|
|
}
|
|
}
|
|
|
|
|
|
/* These vars are initialized by the pipeline controller for use by
|
|
* MCU_output_catcher.
|
|
* To avoid a lot of row-pointer overhead, we cram as many MCUs into each
|
|
* row of whole_scan_MCUs as we can get without exceeding 64KB per row.
|
|
*/
|
|
|
|
#define MAX_WHOLE_ROW_BLOCKS ((int) (65500 / SIZEOF(JBLOCK))) /* max blocks/row */
|
|
|
|
static big_barray_ptr whole_scan_MCUs; /* Big array for saving the MCUs */
|
|
static int MCUs_in_big_row; /* # of MCUs in each row of whole_scan_MCUs */
|
|
static long next_whole_row; /* next row to access in whole_scan_MCUs */
|
|
static int next_MCU_index; /* next MCU in current row */
|
|
|
|
|
|
METHODDEF void
|
|
MCU_output_catcher (compress_info_ptr cinfo, JBLOCK *MCU_data)
|
|
/* Output method for siphoning off extract_MCUs output into a big array */
|
|
{
|
|
static JBLOCKARRAY rowptr;
|
|
|
|
if (next_MCU_index >= MCUs_in_big_row) {
|
|
rowptr = (*cinfo->emethods->access_big_barray) (whole_scan_MCUs,
|
|
next_whole_row, TRUE);
|
|
next_whole_row++;
|
|
next_MCU_index = 0;
|
|
}
|
|
|
|
/*
|
|
* note that on 80x86, the cast applied to MCU_data implies
|
|
* near to far pointer conversion.
|
|
*/
|
|
jcopy_block_row((JBLOCKROW) MCU_data,
|
|
rowptr[0] + next_MCU_index * cinfo->blocks_in_MCU,
|
|
(long) cinfo->blocks_in_MCU);
|
|
next_MCU_index++;
|
|
}
|
|
|
|
|
|
METHODDEF void
|
|
dump_scan_MCUs (compress_info_ptr cinfo, MCU_output_method_ptr output_method)
|
|
/* Dump the MCUs saved in whole_scan_MCUs to the output method. */
|
|
/* The method may be either the entropy encoder or some routine supplied */
|
|
/* by the entropy optimizer. */
|
|
{
|
|
/* On an 80x86 machine, the entropy encoder expects the passed data block
|
|
* to be in NEAR memory (for performance reasons), so we have to copy it
|
|
* back from the big array to a local array. On less brain-damaged CPUs
|
|
* we needn't do that.
|
|
*/
|
|
#ifdef NEED_FAR_POINTERS
|
|
JBLOCK MCU_data[MAX_BLOCKS_IN_MCU];
|
|
#endif
|
|
long mcurow, mcuindex, next_row;
|
|
int next_index;
|
|
JBLOCKARRAY rowptr = NULL; /* init only to suppress compiler complaint */
|
|
|
|
next_row = 0;
|
|
next_index = MCUs_in_big_row;
|
|
|
|
for (mcurow = 0; mcurow < cinfo->MCU_rows_in_scan; mcurow++) {
|
|
for (mcuindex = 0; mcuindex < cinfo->MCUs_per_row; mcuindex++) {
|
|
if (next_index >= MCUs_in_big_row) {
|
|
rowptr = (*cinfo->emethods->access_big_barray) (whole_scan_MCUs,
|
|
next_row, FALSE);
|
|
next_row++;
|
|
next_index = 0;
|
|
}
|
|
#ifdef NEED_FAR_POINTERS
|
|
jcopy_block_row(rowptr[0] + next_index * cinfo->blocks_in_MCU,
|
|
(JBLOCKROW) MCU_data, /* note cast */
|
|
(long) cinfo->blocks_in_MCU);
|
|
(*output_method) (cinfo, MCU_data);
|
|
#else
|
|
(*output_method) (cinfo, rowptr[0] + next_index * cinfo->blocks_in_MCU);
|
|
#endif
|
|
next_index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Compression pipeline controller used for single-scan files
|
|
* with no optimization of entropy parameters.
|
|
*/
|
|
|
|
METHODDEF void
|
|
single_ccontroller (compress_info_ptr cinfo)
|
|
{
|
|
int rows_in_mem; /* # of sample rows in full-size buffers */
|
|
long fullsize_width; /* # of samples per row in full-size buffers */
|
|
long cur_pixel_row; /* counts # of pixel rows processed */
|
|
long mcu_rows_output; /* # of MCU rows actually emitted */
|
|
int mcu_rows_per_loop; /* # of MCU rows processed per outer loop */
|
|
/* Work buffer for pre-subsampling data (see comments at head of file) */
|
|
JSAMPIMAGE fullsize_data[2];
|
|
/* Work buffer for subsampled data */
|
|
JSAMPIMAGE subsampled_data;
|
|
int rows_this_time;
|
|
short ci, whichss, i;
|
|
|
|
/* Prepare for single scan containing all components */
|
|
if (cinfo->num_components > MAX_COMPS_IN_SCAN)
|
|
ERREXIT(cinfo->emethods, "Too many components for interleaved 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];
|
|
}
|
|
if (cinfo->comps_in_scan == 1) {
|
|
noninterleaved_scan_setup(cinfo);
|
|
/* Vk block rows constitute the same number of MCU 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 contains 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: */
|
|
/* fullsize_data is sample data before subsampling */
|
|
alloc_sampling_buffer(cinfo, fullsize_data, fullsize_width);
|
|
/* subsampled_data is sample data after subsampling */
|
|
subsampled_data = (JSAMPIMAGE) (*cinfo->emethods->alloc_small)
|
|
(cinfo->num_components * SIZEOF(JSAMPARRAY));
|
|
for (ci = 0; ci < cinfo->num_components; ci++) {
|
|
subsampled_data[ci] = (*cinfo->emethods->alloc_small_sarray)
|
|
(cinfo->comp_info[ci].subsampled_width,
|
|
(long) (cinfo->comp_info[ci].v_samp_factor * DCTSIZE));
|
|
}
|
|
|
|
/* Tell the memory manager to instantiate big arrays.
|
|
* We don't need any big arrays in this controller,
|
|
* but some other module (like the input file reader) 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 */
|
|
|
|
/* Initialize output file & do per-scan object init */
|
|
|
|
(*cinfo->methods->write_scan_header) (cinfo);
|
|
cinfo->methods->entropy_output = cinfo->methods->write_jpeg_data;
|
|
(*cinfo->methods->entropy_encoder_init) (cinfo);
|
|
(*cinfo->methods->subsample_init) (cinfo);
|
|
(*cinfo->methods->extract_init) (cinfo);
|
|
|
|
/* Loop over input image: rows_in_mem pixel rows are processed per loop */
|
|
|
|
mcu_rows_output = 0;
|
|
whichss = 1; /* arrange to start with fullsize_data[0] */
|
|
|
|
for (cur_pixel_row = 0; cur_pixel_row < cinfo->image_height;
|
|
cur_pixel_row += rows_in_mem) {
|
|
whichss ^= 1; /* switch to other fullsize_data buffer */
|
|
|
|
/* Obtain rows_this_time pixel rows and expand to rows_in_mem rows. */
|
|
/* Then we have exactly DCTSIZE row groups for subsampling. */
|
|
rows_this_time = (int) MIN((long) rows_in_mem,
|
|
cinfo->image_height - cur_pixel_row);
|
|
|
|
(*cinfo->methods->get_sample_rows) (cinfo, rows_this_time,
|
|
fullsize_data[whichss]);
|
|
(*cinfo->methods->edge_expand) (cinfo,
|
|
cinfo->image_width, rows_this_time,
|
|
fullsize_width, rows_in_mem,
|
|
fullsize_data[whichss]);
|
|
|
|
/* Subsample the data (all components) */
|
|
/* First time through is a special case */
|
|
|
|
if (cur_pixel_row) {
|
|
/* Subsample last row group of previous set */
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) DCTSIZE, (short) (DCTSIZE+1), (short) 0,
|
|
(short) (DCTSIZE-1));
|
|
/* and dump the previous set's subsampled data */
|
|
(*cinfo->methods->extract_MCUs) (cinfo, subsampled_data,
|
|
mcu_rows_per_loop,
|
|
cinfo->methods->entropy_encode);
|
|
mcu_rows_output += mcu_rows_per_loop;
|
|
/* Subsample first row group of this set */
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) (DCTSIZE+1), (short) 0, (short) 1,
|
|
(short) 0);
|
|
} else {
|
|
/* Subsample first row group with dummy above-context */
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) (-1), (short) 0, (short) 1,
|
|
(short) 0);
|
|
}
|
|
/* Subsample second through next-to-last row groups of this set */
|
|
for (i = 1; i <= DCTSIZE-2; i++) {
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) (i-1), (short) i, (short) (i+1),
|
|
(short) i);
|
|
}
|
|
} /* end of outer loop */
|
|
|
|
/* Subsample the last row group with dummy below-context */
|
|
/* Note whichss points to last buffer side used */
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) (DCTSIZE-2), (short) (DCTSIZE-1), (short) (-1),
|
|
(short) (DCTSIZE-1));
|
|
/* Dump the remaining data (may be less than full height if uninterleaved) */
|
|
(*cinfo->methods->extract_MCUs) (cinfo, subsampled_data,
|
|
(int) (cinfo->MCU_rows_in_scan - mcu_rows_output),
|
|
cinfo->methods->entropy_encode);
|
|
|
|
/* Finish output file */
|
|
(*cinfo->methods->extract_term) (cinfo);
|
|
(*cinfo->methods->subsample_term) (cinfo);
|
|
(*cinfo->methods->entropy_encoder_term) (cinfo);
|
|
(*cinfo->methods->write_scan_trailer) (cinfo);
|
|
|
|
/* Release working memory */
|
|
free_sampling_buffer(cinfo, fullsize_data);
|
|
for (ci = 0; ci < cinfo->num_components; ci++) {
|
|
(*cinfo->emethods->free_small_sarray)
|
|
(subsampled_data[ci],
|
|
(long) (cinfo->comp_info[ci].v_samp_factor * DCTSIZE));
|
|
}
|
|
(*cinfo->emethods->free_small) ((void *) subsampled_data);
|
|
}
|
|
|
|
|
|
/*
|
|
* Compression pipeline controller used for single-scan files
|
|
* with optimization of entropy parameters.
|
|
*/
|
|
|
|
#ifdef ENTROPY_OPT_SUPPORTED
|
|
|
|
METHODDEF void
|
|
single_eopt_ccontroller (compress_info_ptr cinfo)
|
|
{
|
|
int rows_in_mem; /* # of sample rows in full-size buffers */
|
|
long fullsize_width; /* # of samples per row in full-size buffers */
|
|
long cur_pixel_row; /* counts # of pixel rows processed */
|
|
long mcu_rows_output; /* # of MCU rows actually emitted */
|
|
int mcu_rows_per_loop; /* # of MCU rows processed per outer loop */
|
|
/* Work buffer for pre-subsampling data (see comments at head of file) */
|
|
JSAMPIMAGE fullsize_data[2];
|
|
/* Work buffer for subsampled data */
|
|
JSAMPIMAGE subsampled_data;
|
|
int rows_this_time;
|
|
int blocks_in_big_row;
|
|
short ci, whichss, i;
|
|
|
|
/* Prepare for single scan containing all components */
|
|
if (cinfo->num_components > MAX_COMPS_IN_SCAN)
|
|
ERREXIT(cinfo->emethods, "Too many components for interleaved 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];
|
|
}
|
|
if (cinfo->comps_in_scan == 1) {
|
|
noninterleaved_scan_setup(cinfo);
|
|
/* Vk block rows constitute the same number of MCU 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 contains 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: */
|
|
/* fullsize_data is sample data before subsampling */
|
|
alloc_sampling_buffer(cinfo, fullsize_data, fullsize_width);
|
|
/* subsampled_data is sample data after subsampling */
|
|
subsampled_data = (JSAMPIMAGE) (*cinfo->emethods->alloc_small)
|
|
(cinfo->num_components * SIZEOF(JSAMPARRAY));
|
|
for (ci = 0; ci < cinfo->num_components; ci++) {
|
|
subsampled_data[ci] = (*cinfo->emethods->alloc_small_sarray)
|
|
(cinfo->comp_info[ci].subsampled_width,
|
|
(long) (cinfo->comp_info[ci].v_samp_factor * DCTSIZE));
|
|
}
|
|
|
|
/* Figure # of MCUs to be packed in a row of whole_scan_MCUs */
|
|
MCUs_in_big_row = MAX_WHOLE_ROW_BLOCKS / cinfo->blocks_in_MCU;
|
|
blocks_in_big_row = MCUs_in_big_row * cinfo->blocks_in_MCU;
|
|
|
|
/* Request a big array: whole_scan_MCUs saves the MCU data for the scan */
|
|
whole_scan_MCUs = (*cinfo->emethods->request_big_barray)
|
|
((long) blocks_in_big_row,
|
|
(long) (cinfo->MCUs_per_row * cinfo->MCU_rows_in_scan
|
|
+ MCUs_in_big_row-1) / MCUs_in_big_row,
|
|
1L); /* unit height is 1 row */
|
|
|
|
next_whole_row = 0; /* init output ptr for MCU_output_catcher */
|
|
next_MCU_index = MCUs_in_big_row; /* forces access on first call! */
|
|
|
|
/* 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 */
|
|
|
|
/* Do per-scan object init */
|
|
|
|
(*cinfo->methods->subsample_init) (cinfo);
|
|
(*cinfo->methods->extract_init) (cinfo);
|
|
|
|
/* Loop over input image: rows_in_mem pixel rows are processed per loop */
|
|
/* MCU data goes into whole_scan_MCUs, not to the entropy encoder */
|
|
|
|
mcu_rows_output = 0;
|
|
whichss = 1; /* arrange to start with fullsize_data[0] */
|
|
|
|
for (cur_pixel_row = 0; cur_pixel_row < cinfo->image_height;
|
|
cur_pixel_row += rows_in_mem) {
|
|
whichss ^= 1; /* switch to other fullsize_data buffer */
|
|
|
|
/* Obtain rows_this_time pixel rows and expand to rows_in_mem rows. */
|
|
/* Then we have exactly DCTSIZE row groups for subsampling. */
|
|
rows_this_time = (int) MIN((long) rows_in_mem,
|
|
cinfo->image_height - cur_pixel_row);
|
|
|
|
(*cinfo->methods->get_sample_rows) (cinfo, rows_this_time,
|
|
fullsize_data[whichss]);
|
|
(*cinfo->methods->edge_expand) (cinfo,
|
|
cinfo->image_width, rows_this_time,
|
|
fullsize_width, rows_in_mem,
|
|
fullsize_data[whichss]);
|
|
|
|
/* Subsample the data (all components) */
|
|
/* First time through is a special case */
|
|
|
|
if (cur_pixel_row) {
|
|
/* Subsample last row group of previous set */
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) DCTSIZE, (short) (DCTSIZE+1), (short) 0,
|
|
(short) (DCTSIZE-1));
|
|
/* and dump the previous set's subsampled data */
|
|
(*cinfo->methods->extract_MCUs) (cinfo, subsampled_data,
|
|
mcu_rows_per_loop,
|
|
MCU_output_catcher);
|
|
mcu_rows_output += mcu_rows_per_loop;
|
|
/* Subsample first row group of this set */
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) (DCTSIZE+1), (short) 0, (short) 1,
|
|
(short) 0);
|
|
} else {
|
|
/* Subsample first row group with dummy above-context */
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) (-1), (short) 0, (short) 1,
|
|
(short) 0);
|
|
}
|
|
/* Subsample second through next-to-last row groups of this set */
|
|
for (i = 1; i <= DCTSIZE-2; i++) {
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) (i-1), (short) i, (short) (i+1),
|
|
(short) i);
|
|
}
|
|
} /* end of outer loop */
|
|
|
|
/* Subsample the last row group with dummy below-context */
|
|
/* Note whichss points to last buffer side used */
|
|
subsample(cinfo, fullsize_data[whichss], subsampled_data, fullsize_width,
|
|
(short) (DCTSIZE-2), (short) (DCTSIZE-1), (short) (-1),
|
|
(short) (DCTSIZE-1));
|
|
/* Dump the remaining data (may be less than full height if uninterleaved) */
|
|
(*cinfo->methods->extract_MCUs) (cinfo, subsampled_data,
|
|
(int) (cinfo->MCU_rows_in_scan - mcu_rows_output),
|
|
MCU_output_catcher);
|
|
|
|
/* Clean up after that stuff, then find the optimal entropy parameters */
|
|
|
|
(*cinfo->methods->extract_term) (cinfo);
|
|
(*cinfo->methods->subsample_term) (cinfo);
|
|
|
|
(*cinfo->methods->entropy_optimize) (cinfo, dump_scan_MCUs);
|
|
|
|
/* Emit scan to output file */
|
|
/* Note: we can't do write_scan_header until entropy parameters are set! */
|
|
|
|
(*cinfo->methods->write_scan_header) (cinfo);
|
|
cinfo->methods->entropy_output = cinfo->methods->write_jpeg_data;
|
|
(*cinfo->methods->entropy_encoder_init) (cinfo);
|
|
dump_scan_MCUs(cinfo, cinfo->methods->entropy_encode);
|
|
(*cinfo->methods->entropy_encoder_term) (cinfo);
|
|
(*cinfo->methods->write_scan_trailer) (cinfo);
|
|
|
|
/* Release working memory */
|
|
free_sampling_buffer(cinfo, fullsize_data);
|
|
for (ci = 0; ci < cinfo->num_components; ci++) {
|
|
(*cinfo->emethods->free_small_sarray)
|
|
(subsampled_data[ci],
|
|
(long) (cinfo->comp_info[ci].v_samp_factor * DCTSIZE));
|
|
}
|
|
(*cinfo->emethods->free_small) ((void *) subsampled_data);
|
|
(*cinfo->emethods->free_big_barray) (whole_scan_MCUs);
|
|
}
|
|
|
|
#endif /* ENTROPY_OPT_SUPPORTED */
|
|
|
|
|
|
/*
|
|
* Compression pipeline controller used for multiple-scan files
|
|
* with no optimization of entropy parameters.
|
|
*/
|
|
|
|
#ifdef MULTISCAN_FILES_SUPPORTED
|
|
|
|
METHODDEF void
|
|
multi_ccontroller (compress_info_ptr cinfo)
|
|
{
|
|
ERREXIT(cinfo->emethods, "Not implemented yet");
|
|
}
|
|
|
|
#endif /* MULTISCAN_FILES_SUPPORTED */
|
|
|
|
|
|
/*
|
|
* Compression pipeline controller used for multiple-scan files
|
|
* with optimization of entropy parameters.
|
|
*/
|
|
|
|
#ifdef MULTISCAN_FILES_SUPPORTED
|
|
#ifdef ENTROPY_OPT_SUPPORTED
|
|
|
|
METHODDEF void
|
|
multi_eopt_ccontroller (compress_info_ptr cinfo)
|
|
{
|
|
ERREXIT(cinfo->emethods, "Not implemented yet");
|
|
}
|
|
|
|
#endif /* ENTROPY_OPT_SUPPORTED */
|
|
#endif /* MULTISCAN_FILES_SUPPORTED */
|
|
|
|
|
|
/*
|
|
* The method selection routine for compression pipeline controllers.
|
|
*/
|
|
|
|
GLOBAL void
|
|
jselcpipeline (compress_info_ptr cinfo)
|
|
{
|
|
if (cinfo->interleave || cinfo->num_components == 1) {
|
|
/* single scan needed */
|
|
#ifdef ENTROPY_OPT_SUPPORTED
|
|
if (cinfo->optimize_coding)
|
|
cinfo->methods->c_pipeline_controller = single_eopt_ccontroller;
|
|
else
|
|
#endif
|
|
cinfo->methods->c_pipeline_controller = single_ccontroller;
|
|
} else {
|
|
/* multiple scans needed */
|
|
#ifdef MULTISCAN_FILES_SUPPORTED
|
|
#ifdef ENTROPY_OPT_SUPPORTED
|
|
if (cinfo->optimize_coding)
|
|
cinfo->methods->c_pipeline_controller = multi_eopt_ccontroller;
|
|
else
|
|
#endif
|
|
cinfo->methods->c_pipeline_controller = multi_ccontroller;
|
|
#else
|
|
ERREXIT(cinfo->emethods, "Multiple-scan support was not compiled");
|
|
#endif
|
|
}
|
|
}
|