unit imjcmaster; { This file contains master control logic for the JPEG compressor. 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). } { Original: jcmaster.c ; Copyright (C) 1991-1997, Thomas G. Lane. } interface {$I imjconfig.inc} uses imjmorecfg, imjinclude, imjdeferr, imjerror, imjutils, imjpeglib; { Initialize master compression control. } {GLOBAL} procedure jinit_c_master_control (cinfo : j_compress_ptr; transcode_only : boolean); implementation { Private state } type c_pass_type = ( main_pass, { input data, also do first output step } huff_opt_pass, { Huffman code optimization pass } output_pass { data output pass } ); type my_master_ptr = ^my_comp_master; my_comp_master = record pub : jpeg_comp_master; { public fields } pass_type : c_pass_type; { the type of the current pass } pass_number : int; { # of passes completed } total_passes : int; { total # of passes needed } scan_number : int; { current index in scan_info[] } end; { Support routines that do various essential calculations. } {LOCAL} procedure initial_setup (cinfo : j_compress_ptr); { Do computations that are needed before master selection phase } var ci : int; compptr : jpeg_component_info_ptr; samplesperrow : long; jd_samplesperrow : JDIMENSION; begin { Sanity check on image dimensions } if (cinfo^.image_height <= 0) or (cinfo^.image_width <= 0) or (cinfo^.num_components <= 0) or (cinfo^.input_components <= 0) then ERREXIT(j_common_ptr(cinfo), JERR_EMPTY_IMAGE); { Make sure image isn't bigger than I can handle } if ( long(cinfo^.image_height) > long(JPEG_MAX_DIMENSION)) or ( long(cinfo^.image_width) > long(JPEG_MAX_DIMENSION)) then ERREXIT1(j_common_ptr(cinfo), JERR_IMAGE_TOO_BIG, uInt(JPEG_MAX_DIMENSION)); { Width of an input scanline must be representable as JDIMENSION. } samplesperrow := long (cinfo^.image_width) * long (cinfo^.input_components); jd_samplesperrow := JDIMENSION (samplesperrow); if ( long(jd_samplesperrow) <> samplesperrow) then ERREXIT(j_common_ptr(cinfo), JERR_WIDTH_OVERFLOW); { For now, precision must match compiled-in value... } if (cinfo^.data_precision <> BITS_IN_JSAMPLE) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PRECISION, cinfo^.data_precision); { Check that number of components won't exceed internal array sizes } if (cinfo^.num_components > MAX_COMPONENTS) then ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, cinfo^.num_components, MAX_COMPONENTS); { Compute maximum sampling factors; check factor validity } cinfo^.max_h_samp_factor := 1; cinfo^.max_v_samp_factor := 1; compptr := jpeg_component_info_ptr(cinfo^.comp_info); for ci := 0 to pred(cinfo^.num_components) do begin if (compptr^.h_samp_factor<=0) or (compptr^.h_samp_factor>MAX_SAMP_FACTOR) or (compptr^.v_samp_factor<=0) or (compptr^.v_samp_factor>MAX_SAMP_FACTOR) then ERREXIT(j_common_ptr(cinfo), JERR_BAD_SAMPLING); { MAX } if cinfo^.max_h_samp_factor > compptr^.h_samp_factor then cinfo^.max_h_samp_factor := cinfo^.max_h_samp_factor else cinfo^.max_h_samp_factor := compptr^.h_samp_factor; { MAX } if cinfo^.max_v_samp_factor > compptr^.v_samp_factor then cinfo^.max_v_samp_factor := cinfo^.max_v_samp_factor else cinfo^.max_v_samp_factor := compptr^.v_samp_factor; Inc(compptr); end; { Compute dimensions of components } compptr := jpeg_component_info_ptr(cinfo^.comp_info); for ci := 0 to pred(cinfo^.num_components) do begin { 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 } compptr^.width_in_blocks := JDIMENSION ( jdiv_round_up(long (cinfo^.image_width) * long (compptr^.h_samp_factor), long (cinfo^.max_h_samp_factor * DCTSIZE)) ); compptr^.height_in_blocks := JDIMENSION ( jdiv_round_up(long (cinfo^.image_height) * long (compptr^.v_samp_factor), long (cinfo^.max_v_samp_factor * DCTSIZE)) ); { Size in samples } compptr^.downsampled_width := JDIMENSION ( jdiv_round_up(long(cinfo^.image_width) * long(compptr^.h_samp_factor), long(cinfo^.max_h_samp_factor)) ); compptr^.downsampled_height := JDIMENSION ( jdiv_round_up(long (cinfo^.image_height) * long(compptr^.v_samp_factor), long (cinfo^.max_v_samp_factor)) ); { Mark component needed (this flag isn't actually used for compression) } compptr^.component_needed := TRUE; Inc(compptr); end; { Compute number of fully interleaved MCU rows (number of times that main controller will call coefficient controller). } cinfo^.total_iMCU_rows := JDIMENSION ( jdiv_round_up(long (cinfo^.image_height), long (cinfo^.max_v_samp_factor*DCTSIZE)) ); end; {$ifdef C_MULTISCAN_FILES_SUPPORTED} {LOCAL} procedure validate_script (cinfo : j_compress_ptr); { Verify that the scan script in cinfo^.scan_info[] is valid; also determine whether it uses progressive JPEG, and set cinfo^.progressive_mode. } type IntRow = array[0..DCTSIZE2-1] of int; introw_ptr = ^IntRow; var {const}scanptr : jpeg_scan_info_ptr; scanno, ncomps, ci, coefi, thisi : int; Ss, Se, Ah, Al : int; component_sent : array[0..MAX_COMPONENTS-1] of boolean; {$ifdef C_PROGRESSIVE_SUPPORTED} last_bitpos_int_ptr : int_ptr; last_bitpos_ptr : introw_ptr; last_bitpos : array[0..MAX_COMPONENTS-1] of IntRow; { -1 until that coefficient has been seen; then last Al for it } { The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that seems wrong: the upper bound ought to depend on data precision. Perhaps they really meant 0..N+1 for N-bit precision. Here we allow 0..10 for 8-bit data; Al larger than 10 results in out-of-range reconstructed DC values during the first DC scan, which might cause problems for some decoders. } {$ifdef BITS_IN_JSAMPLE_IS_8} const MAX_AH_AL = 10; {$else} const MAX_AH_AL = 13; {$endif} {$endif} begin if (cinfo^.num_scans <= 0) then ERREXIT1(j_common_ptr(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) or (scanptr^.Se <> DCTSIZE2-1) then begin {$ifdef C_PROGRESSIVE_SUPPORTED} cinfo^.progressive_mode := TRUE; last_bitpos_int_ptr := @(last_bitpos[0][0]); for ci := 0 to pred(cinfo^.num_components) do for coefi := 0 to pred(DCTSIZE2) do begin last_bitpos_int_ptr^ := -1; Inc(last_bitpos_int_ptr); end; {$else} ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); {$endif} end else begin cinfo^.progressive_mode := FALSE; for ci := 0 to pred(cinfo^.num_components) do component_sent[ci] := FALSE; end; for scanno := 1 to cinfo^.num_scans do begin { Validate component indexes } ncomps := scanptr^.comps_in_scan; if (ncomps <= 0) or (ncomps > MAX_COMPS_IN_SCAN) then ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN); for ci := 0 to pred(ncomps) do begin thisi := scanptr^.component_index[ci]; if (thisi < 0) or (thisi >= cinfo^.num_components) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); { Components must appear in SOF order within each scan } if (ci > 0) and (thisi <= scanptr^.component_index[ci-1]) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); end; { Validate progression parameters } Ss := scanptr^.Ss; Se := scanptr^.Se; Ah := scanptr^.Ah; Al := scanptr^.Al; if (cinfo^.progressive_mode) then begin {$ifdef C_PROGRESSIVE_SUPPORTED} if (Ss < 0) or (Ss >= DCTSIZE2) or (Se < Ss) or (Se >= DCTSIZE2) or (Ah < 0) or (Ah > MAX_AH_AL) or (Al < 0) or (Al > MAX_AH_AL) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); if (Ss < 0) or (Ss >= DCTSIZE2) or (Se < Ss) or (Se >= DCTSIZE2) or (Ah < 0) or (Ah > MAX_AH_AL) or (Al < 0) or (Al > MAX_AH_AL) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); if (Ss = 0) then begin if (Se <> 0) then { DC and AC together not OK } ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); end else begin if (ncomps <> 1) then { AC scans must be for only one component } ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); end; for ci := 0 to pred(ncomps) do begin last_bitpos_ptr := @( last_bitpos[scanptr^.component_index[ci]]); if (Ss <> 0) and (last_bitpos_ptr^[0] < 0) then { AC without prior DC scan } ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); for coefi := Ss to Se do begin if (last_bitpos_ptr^[coefi] < 0) then begin { first scan of this coefficient } if (Ah <> 0) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); end else begin { not first scan } if (Ah <> last_bitpos_ptr^[coefi]) or (Al <> Ah-1) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); end; last_bitpos_ptr^[coefi] := Al; end; end; {$endif} end else begin { For sequential JPEG, all progression parameters must be these: } if (Ss <> 0) or (Se <> DCTSIZE2-1) or (Ah <> 0) or (Al <> 0) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); { Make sure components are not sent twice } for ci := 0 to pred(ncomps) do begin thisi := scanptr^.component_index[ci]; if (component_sent[thisi]) then ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); component_sent[thisi] := TRUE; end; end; Inc(scanptr); end; { Now verify that everything got sent. } if (cinfo^.progressive_mode) then begin {$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 to pred(cinfo^.num_components) do begin if (last_bitpos[ci][0] < 0) then ERREXIT(j_common_ptr(cinfo), JERR_MISSING_DATA); end; {$endif} end else begin for ci := 0 to pred(cinfo^.num_components) do begin if (not component_sent[ci]) then ERREXIT(j_common_ptr(cinfo), JERR_MISSING_DATA); end; end; end; {$endif} { C_MULTISCAN_FILES_SUPPORTED } {LOCAL} procedure select_scan_parameters (cinfo : j_compress_ptr); { Set up the scan parameters for the current scan } var master : my_master_ptr; {const} scanptr : jpeg_scan_info_ptr; ci : int; var comp_infos : jpeg_component_info_list_ptr; begin {$ifdef C_MULTISCAN_FILES_SUPPORTED} if (cinfo^.scan_info <> NIL) then begin { Prepare for current scan --- the script is already validated } master := my_master_ptr (cinfo^.master); scanptr := cinfo^.scan_info; Inc(scanptr, master^.scan_number); cinfo^.comps_in_scan := scanptr^.comps_in_scan; comp_infos := cinfo^.comp_info; for ci := 0 to pred(scanptr^.comps_in_scan) do begin cinfo^.cur_comp_info[ci] := @(comp_infos^[scanptr^.component_index[ci]]); end; cinfo^.Ss := scanptr^.Ss; cinfo^.Se := scanptr^.Se; cinfo^.Ah := scanptr^.Ah; cinfo^.Al := scanptr^.Al; end else {$endif} begin { Prepare for single sequential-JPEG scan containing all components } if (cinfo^.num_components > MAX_COMPS_IN_SCAN) then ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, cinfo^.num_components, MAX_COMPS_IN_SCAN); cinfo^.comps_in_scan := cinfo^.num_components; comp_infos := cinfo^.comp_info; for ci := 0 to pred(cinfo^.num_components) do begin cinfo^.cur_comp_info[ci] := @(comp_infos^[ci]); end; cinfo^.Ss := 0; cinfo^.Se := DCTSIZE2-1; cinfo^.Ah := 0; cinfo^.Al := 0; end; end; {LOCAL} procedure per_scan_setup (cinfo : j_compress_ptr); { Do computations that are needed before processing a JPEG scan } { cinfo^.comps_in_scan and cinfo^.cur_comp_info[] are already set } var ci, mcublks, tmp : int; compptr : jpeg_component_info_ptr; nominal : long; begin if (cinfo^.comps_in_scan = 1) then begin { Noninterleaved (single-component) scan } compptr := cinfo^.cur_comp_info[0]; { Overall image size in MCUs } cinfo^.MCUs_per_row := compptr^.width_in_blocks; cinfo^.MCU_rows_in_scan := compptr^.height_in_blocks; { For noninterleaved scan, always one block per MCU } compptr^.MCU_width := 1; compptr^.MCU_height := 1; compptr^.MCU_blocks := 1; compptr^.MCU_sample_width := DCTSIZE; compptr^.last_col_width := 1; { For noninterleaved scans, it is convenient to define last_row_height as the number of block rows present in the last iMCU row. } tmp := int (compptr^.height_in_blocks) mod compptr^.v_samp_factor; if (tmp = 0) then tmp := compptr^.v_samp_factor; compptr^.last_row_height := tmp; { Prepare array describing MCU composition } cinfo^.blocks_in_MCU := 1; cinfo^.MCU_membership[0] := 0; end else begin { Interleaved (multi-component) scan } if (cinfo^.comps_in_scan <= 0) or (cinfo^.comps_in_scan > MAX_COMPS_IN_SCAN) then ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, cinfo^.comps_in_scan, MAX_COMPS_IN_SCAN); { Overall image size in MCUs } cinfo^.MCUs_per_row := JDIMENSION ( jdiv_round_up( long (cinfo^.image_width), long (cinfo^.max_h_samp_factor*DCTSIZE)) ); cinfo^.MCU_rows_in_scan := JDIMENSION ( jdiv_round_up( long (cinfo^.image_height), long (cinfo^.max_v_samp_factor*DCTSIZE)) ); cinfo^.blocks_in_MCU := 0; for ci := 0 to pred(cinfo^.comps_in_scan) do begin compptr := cinfo^.cur_comp_info[ci]; { Sampling factors give # of blocks of component in each MCU } compptr^.MCU_width := compptr^.h_samp_factor; compptr^.MCU_height := compptr^.v_samp_factor; compptr^.MCU_blocks := compptr^.MCU_width * compptr^.MCU_height; compptr^.MCU_sample_width := compptr^.MCU_width * DCTSIZE; { Figure number of non-dummy blocks in last MCU column & row } tmp := int (compptr^.width_in_blocks) mod compptr^.MCU_width; if (tmp = 0) then tmp := compptr^.MCU_width; compptr^.last_col_width := tmp; tmp := int (compptr^.height_in_blocks) mod compptr^.MCU_height; if (tmp = 0) then tmp := compptr^.MCU_height; compptr^.last_row_height := tmp; { Prepare array describing MCU composition } mcublks := compptr^.MCU_blocks; if (cinfo^.blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) then ERREXIT(j_common_ptr(cinfo), JERR_BAD_MCU_SIZE); while (mcublks > 0) do begin Dec(mcublks); cinfo^.MCU_membership[cinfo^.blocks_in_MCU] := ci; Inc(cinfo^.blocks_in_MCU); end; end; end; { Convert restart specified in rows to actual MCU count. } { Note that count must fit in 16 bits, so we provide limiting. } if (cinfo^.restart_in_rows > 0) then begin nominal := long(cinfo^.restart_in_rows) * long(cinfo^.MCUs_per_row); if nominal < long(65535) then cinfo^.restart_interval := uInt (nominal) else cinfo^.restart_interval := long(65535); end; end; { 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. We also set is_last_pass to indicate whether any more passes will be required. } {METHODDEF} procedure prepare_for_pass (cinfo : j_compress_ptr); var master : my_master_ptr; var fallthrough : boolean; begin master := my_master_ptr (cinfo^.master); fallthrough := true; case (master^.pass_type) of main_pass: begin { 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 (not cinfo^.raw_data_in) then begin cinfo^.cconvert^.start_pass (cinfo); cinfo^.downsample^.start_pass (cinfo); cinfo^.prep^.start_pass (cinfo, JBUF_PASS_THRU); end; cinfo^.fdct^.start_pass (cinfo); cinfo^.entropy^.start_pass (cinfo, cinfo^.optimize_coding); if master^.total_passes > 1 then cinfo^.coef^.start_pass (cinfo, JBUF_SAVE_AND_PASS) else cinfo^.coef^.start_pass (cinfo, JBUF_PASS_THRU); cinfo^.main^.start_pass (cinfo, JBUF_PASS_THRU); if (cinfo^.optimize_coding) then begin { No immediate data output; postpone writing frame/scan headers } master^.pub.call_pass_startup := FALSE; end else begin { Will write frame/scan headers at first jpeg_write_scanlines call } master^.pub.call_pass_startup := TRUE; end; end; {$ifdef ENTROPY_OPT_SUPPORTED} huff_opt_pass, output_pass: begin if (master^.pass_type = huff_opt_pass) then begin { Do Huffman optimization for a scan after the first one. } select_scan_parameters(cinfo); per_scan_setup(cinfo); if (cinfo^.Ss <> 0) or (cinfo^.Ah = 0) or (cinfo^.arith_code) then begin cinfo^.entropy^.start_pass (cinfo, TRUE); cinfo^.coef^.start_pass (cinfo, JBUF_CRANK_DEST); master^.pub.call_pass_startup := FALSE; fallthrough := false; end; { Special case: Huffman DC refinement scans need no Huffman table and therefore we can skip the optimization pass for them. } if fallthrough then begin master^.pass_type := output_pass; Inc(master^.pass_number); {FALLTHROUGH} end; end; {$else} output_pass: begin {$endif} if fallthrough then begin { Do a data-output pass. } { We need not repeat per-scan setup if prior optimization pass did it. } if (not cinfo^.optimize_coding) then begin select_scan_parameters(cinfo); per_scan_setup(cinfo); end; 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) then cinfo^.marker^.write_frame_header (cinfo); cinfo^.marker^.write_scan_header (cinfo); master^.pub.call_pass_startup := FALSE; end; end; else ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); end; master^.pub.is_last_pass := (master^.pass_number = master^.total_passes-1); { Set up progress monitor's pass info if present } if (cinfo^.progress <> NIL) then begin cinfo^.progress^.completed_passes := master^.pass_number; cinfo^.progress^.total_passes := master^.total_passes; end; end; { Special start-of-pass hook. This is called by jpeg_write_scanlines if call_pass_startup is TRUE. In single-pass processing, we need this hook because we don't want to write frame/scan headers during jpeg_start_compress; we want to let the application write COM markers etc. between jpeg_start_compress and the jpeg_write_scanlines loop. In multi-pass processing, this routine is not used. } {METHODDEF} procedure pass_startup (cinfo : j_compress_ptr); begin cinfo^.master^.call_pass_startup := FALSE; { reset flag so call only once } cinfo^.marker^.write_frame_header (cinfo); cinfo^.marker^.write_scan_header (cinfo); end; { Finish up at end of pass. } {METHODDEF} procedure finish_pass_master (cinfo : j_compress_ptr); var master : my_master_ptr; begin master := my_master_ptr (cinfo^.master); { 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 } case (master^.pass_type) of main_pass: begin { next pass is either output of scan 0 (after optimization) or output of scan 1 (if no optimization). } master^.pass_type := output_pass; if (not cinfo^.optimize_coding) then Inc(master^.scan_number); end; huff_opt_pass: { next pass is always output of current scan } master^.pass_type := output_pass; output_pass: begin { next pass is either optimization or output of next scan } if (cinfo^.optimize_coding) then master^.pass_type := huff_opt_pass; Inc(master^.scan_number); end; end; Inc(master^.pass_number); end; { Initialize master compression control. } {GLOBAL} procedure jinit_c_master_control (cinfo : j_compress_ptr; transcode_only : boolean); var master : my_master_ptr; begin master := my_master_ptr( cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, SIZEOF(my_comp_master)) ); cinfo^.master := jpeg_comp_master_ptr(master); 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); if (cinfo^.scan_info <> NIL) then begin {$ifdef C_MULTISCAN_FILES_SUPPORTED} validate_script(cinfo); {$else} ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); {$endif} end else begin cinfo^.progressive_mode := FALSE; cinfo^.num_scans := 1; end; if (cinfo^.progressive_mode) then { TEMPORARY HACK ??? } cinfo^.optimize_coding := TRUE; { assume default tables no good for progressive mode } { Initialize my private state } if (transcode_only) then begin { no main pass in transcoding } if (cinfo^.optimize_coding) then master^.pass_type := huff_opt_pass else master^.pass_type := output_pass; end else begin { for normal compression, first pass is always this type: } master^.pass_type := main_pass; end; master^.scan_number := 0; master^.pass_number := 0; if (cinfo^.optimize_coding) then master^.total_passes := cinfo^.num_scans * 2 else master^.total_passes := cinfo^.num_scans; end; end.