EVOLUTION-MANAGER
Edit File: JPEG_band.cpp
/* * Copyright (c) 2002-2012, California Institute of Technology. * All rights reserved. Based on Government Sponsored Research under contracts NAS7-1407 and/or NAS7-03001. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * 3. Neither the name of the California Institute of Technology (Caltech), its operating division the Jet Propulsion Laboratory (JPL), * the National Aeronautics and Space Administration (NASA), nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE CALIFORNIA INSTITUTE OF TECHNOLOGY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Copyright 2014-2015 Esri * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * JPEG band * JPEG page compression and decompression functions, gets compiled twice * once directly and once through inclusion from JPEG12_band.cpp * LIBJPEG_12_H is defined if both 8 and 12 bit JPEG will be supported * JPEG12_ON is defined for the 12 bit versions */ #include "marfa.h" #include <setjmp.h> CPL_C_START #include <jpeglib.h> CPL_C_END CPL_CVSID("$Id: JPEG_band.cpp 35897 2016-10-24 11:54:24Z goatbar $"); NAMESPACE_MRF_START typedef struct MRFJPEGErrorStruct { jmp_buf setjmpBuffer; MRFJPEGErrorStruct() { memset(&setjmpBuffer, 0, sizeof(setjmpBuffer)); } } MRFJPEGErrorStruct; /** *\brief Called when jpeg wants to report a warning * msgLevel can be: * -1 Corrupt data * 0 always display * 1... Trace level */ static void emitMessage(j_common_ptr cinfo, int msgLevel) { if (msgLevel > 0) return; // No trace msgs // There can be many warnings, just print the first one if (cinfo->err->num_warnings++ >1) return; char buffer[JMSG_LENGTH_MAX]; cinfo->err->format_message(cinfo, buffer); CPLError(CE_Failure, CPLE_AppDefined, "%s", buffer); } static void errorExit(j_common_ptr cinfo) { MRFJPEGErrorStruct* psErrorStruct = (MRFJPEGErrorStruct* ) cinfo->client_data; // format the warning message char buffer[JMSG_LENGTH_MAX]; cinfo->err->format_message(cinfo, buffer); CPLError(CE_Failure, CPLE_AppDefined, "%s", buffer); // return control to the setjmp point longjmp(psErrorStruct->setjmpBuffer, 1); } /** *\brief Do nothing stub function for JPEG library, called */ static void stub_source_dec(j_decompress_ptr /*cinfo*/) {} /** *\brief: This function is supposed to do refilling of the input buffer, * but as we provided everything at the beginning, if it is called, then * we have an error. */ static boolean fill_input_buffer_dec(j_decompress_ptr cinfo) { if (0 != cinfo->src->bytes_in_buffer) return TRUE; CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG stream"); return FALSE; } /** *\brief: Do nothing stub function for JPEG library, not called */ static void skip_input_data_dec(j_decompress_ptr /*cinfo*/, long /*l*/) {}; // Destination should be already set up static void init_or_terminate_destination(j_compress_ptr /*cinfo*/) {} // Called if the buffer provided is too small static boolean empty_output_buffer(j_compress_ptr /*cinfo*/) { std::cerr << "JPEG Output buffer empty called\n"; return FALSE; } /* *\brief Compress a JPEG page in memory * * It handles byte or 12 bit data, grayscale, RGB, CMYK, multispectral * * Returns the compressed size in dest.size */ #if defined(JPEG12_ON) CPLErr JPEG_Codec::CompressJPEG12(buf_mgr &dst, buf_mgr &src) #else CPLErr JPEG_Codec::CompressJPEG(buf_mgr &dst, buf_mgr &src) #endif { // The cinfo should stay open and reside in the DS, since it can be left initialized // It saves some time because it has the tables initialized struct jpeg_compress_struct cinfo; MRFJPEGErrorStruct sErrorStruct; struct jpeg_error_mgr sJErr; ILSize sz = img.pagesize; jpeg_destination_mgr jmgr; jmgr.next_output_byte = (JOCTET *)dst.buffer; jmgr.free_in_buffer = dst.size; jmgr.init_destination = init_or_terminate_destination; jmgr.empty_output_buffer = empty_output_buffer; jmgr.term_destination = init_or_terminate_destination; // Look at the source of this, some interesting tidbits cinfo.err = jpeg_std_error(&sJErr); sJErr.error_exit = errorExit; sJErr.emit_message = emitMessage; cinfo.client_data = (void *)&(sErrorStruct); jpeg_create_compress(&cinfo); cinfo.dest = &jmgr; // The page specific info, size and color spaces cinfo.image_width = sz.x; cinfo.image_height = sz.y; cinfo.input_components = sz.c; switch (cinfo.input_components) { case 1:cinfo.in_color_space = JCS_GRAYSCALE; break; case 3:cinfo.in_color_space = JCS_RGB; break; // Stored as YCbCr 4:2:0 by default default: cinfo.in_color_space = JCS_UNKNOWN; // 2, 4-10 bands } // Set all required fields and overwrite the ones we want to change jpeg_set_defaults(&cinfo); // Override certain settings jpeg_set_quality(&cinfo, img.quality, TRUE); cinfo.dct_method = JDCT_FLOAT; // Pretty fast and precise cinfo.optimize_coding = optimize; // Set "OPTIMIZE=TRUE" in OPTIONS, default for 12bit // Do we explicitly turn off the YCC color and downsampling? if (cinfo.in_color_space == JCS_RGB) { if (rgb) { // Stored as RGB jpeg_set_colorspace(&cinfo, JCS_RGB); // Huge files } else if (sameres) { // YCC, somewhat larger files with improved color spatial detail cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 1; // Enabling these lines will make the color components use the same tables as Y, even larger file with slightly better color depth detail // cinfo.comp_info[1].quant_tbl_no = 0; // cinfo.comp_info[2].quant_tbl_no = 0; // cinfo.comp_info[1].dc_tbl_no = 0; // cinfo.comp_info[2].dc_tbl_no = 0; // cinfo.comp_info[1].ac_tbl_no = 0; // cinfo.comp_info[2].ac_tbl_no = 0; } } int linesize = cinfo.image_width * cinfo.input_components * ((cinfo.data_precision == 8) ? 1 : 2); JSAMPROW *rowp = (JSAMPROW *)CPLMalloc(sizeof(JSAMPROW)*sz.y); for (int i = 0; i < sz.y; i++) rowp[i] = (JSAMPROW)(src.buffer + i*linesize); if (setjmp(sErrorStruct.setjmpBuffer)) { CPLError(CE_Failure, CPLE_AppDefined, "MRF: JPEG compression error"); jpeg_destroy_compress(&cinfo); CPLFree(rowp); return CE_Failure; } jpeg_start_compress(&cinfo, TRUE); jpeg_write_scanlines(&cinfo, rowp, sz.y); jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); CPLFree(rowp); // Figure out the size dst.size -= jmgr.free_in_buffer; return CE_None; } /** *\brief In memory decompression of JPEG file * * @param data pointer to output buffer * @param png pointer to PNG in memory * @param sz if non-zero, test that uncompressed data fits in the buffer. */ #if defined(JPEG12_ON) CPLErr JPEG_Codec::DecompressJPEG12(buf_mgr &dst, buf_mgr &isrc) #else CPLErr JPEG_Codec::DecompressJPEG(buf_mgr &dst, buf_mgr &isrc) #endif { int nbands = img.pagesize.c; // Locals, clean up after themselves jpeg_decompress_struct cinfo; MRFJPEGErrorStruct sErrorStruct; struct jpeg_error_mgr sJErr; memset(&cinfo, 0, sizeof(cinfo)); struct jpeg_source_mgr src; cinfo.err = jpeg_std_error( &sJErr ); sJErr.error_exit = errorExit; sJErr.emit_message = emitMessage; cinfo.client_data = (void *) &(sErrorStruct); src.next_input_byte = (JOCTET *)isrc.buffer; src.bytes_in_buffer = isrc.size; src.term_source = stub_source_dec; src.init_source = stub_source_dec; src.skip_input_data = skip_input_data_dec; src.fill_input_buffer = fill_input_buffer_dec; src.resync_to_restart = jpeg_resync_to_restart; jpeg_create_decompress(&cinfo); if (setjmp(sErrorStruct.setjmpBuffer)) { CPLError(CE_Failure, CPLE_AppDefined, "MRF: Error reading JPEG page"); jpeg_destroy_decompress(&cinfo); return CE_Failure; } cinfo.src = &src; jpeg_read_header(&cinfo, TRUE); // Use float, it is actually faster than the ISLOW method by a tiny bit cinfo.dct_method = JDCT_FLOAT; // // Tolerate different input if we can do the conversion // Gray and RGB for example // This also means that a RGB MRF can be read as grayscale and vice versa // If libJPEG can't convert it will throw an error // if (nbands == 3 && cinfo.num_components != nbands) cinfo.out_color_space = JCS_RGB; if (nbands == 1 && cinfo.num_components != nbands) cinfo.out_color_space = JCS_GRAYSCALE; int linesize = cinfo.image_width * nbands * ((cinfo.data_precision == 8) ? 1 : 2); jpeg_start_decompress(&cinfo); // We have a mismatch between the real and the declared data format // warn and fail if output buffer is too small if (linesize*cinfo.image_height != dst.size) { CPLError(CE_Warning, CPLE_AppDefined, "MRF: read JPEG size is wrong"); if (linesize*cinfo.image_height > dst.size) { CPLError(CE_Failure, CPLE_AppDefined, "MRF: JPEG decompress buffer overflow"); jpeg_destroy_decompress(&cinfo); return CE_Failure; } } // Decompress, two lines at a time is what libjpeg does while (cinfo.output_scanline < cinfo.image_height) { char *rp[2]; rp[0] = (char *)dst.buffer + linesize*cinfo.output_scanline; rp[1] = rp[0] + linesize; // if this fails, it calls the error handler // which will report an error jpeg_read_scanlines(&cinfo, JSAMPARRAY(rp), 2); } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); return CE_None; } // This part gets compiled only once #if !defined(JPEG12_ON) // Type dependent dispachers CPLErr JPEG_Band::Decompress(buf_mgr &dst, buf_mgr &src) { #if defined(LIBJPEG_12_H) if (GDT_Byte != img.dt) return codec.DecompressJPEG12(dst, src); #endif return codec.DecompressJPEG(dst, src); } CPLErr JPEG_Band::Compress(buf_mgr &dst, buf_mgr &src) { #if defined(LIBJPEG_12_H) if (GDT_Byte != img.dt) return codec.CompressJPEG12(dst, src); #endif return codec.CompressJPEG(dst, src); } // PHOTOMETRIC == MULTISPECTRAL turns off YCbCr conversion and downsampling JPEG_Band::JPEG_Band( GDALMRFDataset *pDS, const ILImage &image, int b, int level ) : GDALMRFRasterBand(pDS, image, b, int(level)), codec(image) { const int nbands = image.pagesize.c; // Check behavior on signed 16bit. Does the libjpeg sign extend? #if defined(LIBJPEG_12_H) if (GDT_Byte != image.dt && GDT_UInt16 != image.dt) #else if (GDT_Byte != image.dt) #endif { CPLError(CE_Failure, CPLE_NotSupported, "Data type not supported by MRF JPEG"); return; } if( nbands == 3 ) { // Only the 3 band JPEG has storage flavors CPLString const &pm = pDS->GetPhotometricInterpretation(); if (pm == "RGB" || pm == "MULTISPECTRAL") { // Explicit RGB or MS codec.rgb = TRUE; codec.sameres = TRUE; } if (pm == "YCC") codec.sameres = TRUE; } if( GDT_Byte == image.dt ) codec.optimize = GetOptlist().FetchBoolean("OPTIMIZE", FALSE) != FALSE; else codec.optimize = true; // Required for 12bit } #endif NAMESPACE_MRF_END