// Decompression startup: read start of JPEG datastream to see what's there. // Need only initialize JPEG object and supply a data source before calling. // // This routine will read as far as the first SOS marker (ie, actual start of // compressed data), and will save all tables and parameters in the JPEG // object. It will also initialize the decompression parameters to default // values, and finally return JPEG_HEADER_OK. On return, the application may // adjust the decompression parameters and then call jpeg_start_decompress. // (Or, if the application only wanted to determine the image parameters, // the data need not be decompressed. In that case, call jpeg_abort or // jpeg_destroy to release any temporary space.) // If an abbreviated (tables only) datastream is presented, the routine will // return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then // re-use the JPEG object to read the abbreviated image datastream(s). // It is unnecessary (but OK) to call jpeg_abort in this case. // The JPEG_SUSPENDED return code only occurs if the data source module // requests suspension of the decompressor. In this case the application // should load more source data and then re-call jpeg_read_header to resume // processing. // If a non-suspending data source is used and require_image is true, then the // return code need not be inspected since only JPEG_HEADER_OK is possible. // // This routine is now just a front end to jpeg_consume_input, with some // extra error checking. public static CONSUME_INPUT jpeg_read_header(jpeg_decompress cinfo, bool require_image) { if (cinfo.global_state != STATE.DSTART && cinfo.global_state != STATE.DINHEADER) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } CONSUME_INPUT retcode = jpeg_consume_input(cinfo); switch (retcode) { case CONSUME_INPUT.JPEG_REACHED_SOS: return(CONSUME_INPUT.JPEG_HEADER_OK); case CONSUME_INPUT.JPEG_REACHED_EOI: if (require_image) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NO_IMAGE); // Complain if application wanted an image } // Reset to start state; it would be safer to require the application to // call jpeg_abort, but we can't change it now for compatibility reasons. // A side effect is to free any temporary memory (there shouldn't be any). jpeg_abort(cinfo); // sets state=DSTART return(CONSUME_INPUT.JPEG_HEADER_TABLES_ONLY); } return(retcode); }
// Consume data in advance of what the decompressor requires. // This can be called at any time once the decompressor object has // been created and a data source has been set up. // // This routine is essentially a state machine that handles a couple // of critical state-transition actions, namely initial setup and // transition from header scanning to ready-for-start_decompress. // All the actual input is done via the input controller's consume_input // method. public static CONSUME_INPUT jpeg_consume_input(jpeg_decompress cinfo) { CONSUME_INPUT retcode = CONSUME_INPUT.JPEG_SUSPENDED; // NB: every possible DSTATE value should be listed in this switch switch (cinfo.global_state) { case STATE.DSTART: // Start-of-datastream actions: reset appropriate modules cinfo.inputctl.reset_input_controller(cinfo); // Initialize application's data source module cinfo.src.init_source(cinfo); cinfo.global_state = STATE.DINHEADER; goto case STATE.DINHEADER; // FALLTHROUGH case STATE.DINHEADER: retcode = cinfo.inputctl.consume_input(cinfo); if (retcode == CONSUME_INPUT.JPEG_REACHED_SOS) { // Found SOS, prepare to decompress // Set up default parameters based on header data default_decompress_parms(cinfo); // Set global state: ready for start_decompress cinfo.global_state = STATE.DREADY; } break; case STATE.DREADY: retcode = CONSUME_INPUT.JPEG_REACHED_SOS; break; // Can't advance past first SOS until start_decompress is called case STATE.DPRELOAD: case STATE.DPRESCAN: case STATE.DSCANNING: case STATE.DRAW_OK: case STATE.DBUFIMAGE: case STATE.DBUFPOST: case STATE.DSTOPPING: retcode = cinfo.inputctl.consume_input(cinfo); break; default: ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); break; } return(retcode); }
// Decompression initialization. // jpeg_read_header must be completed before calling this. // // If a multipass operating mode was selected, this will do all but the // last pass, and thus may take a great deal of time. // // Returns false if suspended. The return value need be inspected only if // a suspending data source is used. public static bool jpeg_start_decompress(jpeg_decompress cinfo) { if (cinfo.global_state == STATE.DREADY) { // First call: initialize master control, select active modules jinit_master_decompress(cinfo); if (cinfo.buffered_image) { // No more work here; expecting jpeg_start_output next cinfo.global_state = STATE.DBUFIMAGE; return(true); } cinfo.global_state = STATE.DPRELOAD; } if (cinfo.global_state == STATE.DPRELOAD) { // If file has multiple scans, absorb them all into the coef buffer if (cinfo.inputctl.has_multiple_scans) { #if D_MULTISCAN_FILES_SUPPORTED for (; ;) { // Call progress monitor hook if present if (cinfo.progress != null) { cinfo.progress.progress_monitor(cinfo); } // Absorb some more input CONSUME_INPUT retcode = cinfo.inputctl.consume_input(cinfo); if (retcode == CONSUME_INPUT.JPEG_SUSPENDED) { return(false); } if (retcode == CONSUME_INPUT.JPEG_REACHED_EOI) { break; } // Advance progress counter if appropriate if (cinfo.progress != null && (retcode == CONSUME_INPUT.JPEG_ROW_COMPLETED || retcode == CONSUME_INPUT.JPEG_REACHED_SOS)) { if (++cinfo.progress.pass_counter >= cinfo.progress.pass_limit) { // jdmaster underestimated number of scans; ratchet up one scan cinfo.progress.pass_limit += (int)cinfo.total_iMCU_rows; } } } #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif // D_MULTISCAN_FILES_SUPPORTED } cinfo.output_scan_number = cinfo.input_scan_number; } else if (cinfo.global_state != STATE.DPRESCAN) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } // Perform any dummy output passes, and set up for the final pass return(output_pass_setup(cinfo)); }
// Read JPEG markers before, between, or after compressed-data scans. // Change state as necessary when a new scan is reached. // Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. // // The consume_input method pointer points either here or to the // coefficient controller's consume_data routine, depending on whether // we are reading a compressed data segment or inter-segment markers. // // Note: This function should NOT return a pseudo SOS marker (with zero // component number) to the caller. A pseudo marker received by // read_markers is processed and then skipped for other markers. static CONSUME_INPUT consume_markers_d_input(jpeg_decompress cinfo) { my_input_controller inputctl = (my_input_controller)cinfo.inputctl; if (inputctl.eoi_reached) { return(CONSUME_INPUT.JPEG_REACHED_EOI); // After hitting EOI, read no further } CONSUME_INPUT val = cinfo.marker.read_markers(cinfo); for (; ;) // Loop to pass pseudo SOS marker { switch (val) { case CONSUME_INPUT.JPEG_REACHED_SOS: // Found SOS if (inputctl.inheaders != 0) { // 1st SOS if (inputctl.inheaders == 1) { initial_setup_d_input(cinfo); // Initialize the decompression codec. We need to do this here so that // any codec-specific fields and function pointers are available to // the rest of the library. jinit_d_codec(cinfo); } if (cinfo.comps_in_scan == 0) // pseudo SOS marker { inputctl.inheaders = 2; break; } inputctl.inheaders = 0; // Note: start_input_pass must be called by jdmaster.cs // before any more input can be consumed. jdapimin.cs is // responsible for enforcing this sequencing. } else { // 2nd or later SOS marker if (!inputctl.has_multiple_scans) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_EOI_EXPECTED); // Oops, I wasn't expecting this! } if (cinfo.comps_in_scan == 0) // unexpected pseudo SOS marker { break; } start_input_pass_d_input(cinfo); } return(val); case CONSUME_INPUT.JPEG_REACHED_EOI: // Found EOI inputctl.eoi_reached = true; if (inputctl.inheaders != 0) { // Tables-only datastream, apparently if (cinfo.marker.saw_SOF) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_SOF_NO_SOS); } } else { // Prevent infinite loop in coef ctlr's decompress_data routine // if user set output_scan_number larger than number of scans. if (cinfo.output_scan_number > cinfo.input_scan_number) { cinfo.output_scan_number = cinfo.input_scan_number; } } return(val); case CONSUME_INPUT.JPEG_SUSPENDED: return(val); } } }