static void prepare_jpeg_decompress(FILE *input, struct jpeg_decompress_struct *dinfo, struct jpeg_error_mgr *jerr) { long i; dinfo->err = jpeg_std_error(jerr); jpeg_create_decompress(dinfo); jpeg_stdio_src(dinfo, input); /* Save custom headers for the compressor, but ignore APP0 & APP14 so * libjpeg can handle them. */ jpeg_save_markers(dinfo, JPEG_COM, 0xFFFF); for (i=1; i<14; i++) { jpeg_save_markers(dinfo, JPEG_APP0+i, 0xFFFF); } jpeg_save_markers(dinfo, JPEG_APP0+15, 0xFFFF); jpeg_read_header(dinfo, TRUE); /* For testing purposes, you can run with the environment variable * OILRGBX in order to trigger libjpeg turbo's JCS_RGBX color space. */ #ifdef JCS_EXTENSIONS if (getenv("OILRGBX") != NULL && dinfo->out_color_space == JCS_RGB) { dinfo->out_color_space = JCS_EXT_RGBX; jpeg_calc_output_dimensions(dinfo); } #endif jpeg_start_decompress(dinfo); }
/* Read the jpeg from file or buffer. */ static int vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only ) { /* Need to read in APP1 (EXIF metadata), APP2 (ICC profile), APP13 * (photoshop IPCT). */ jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 1, 0xffff ); jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 2, 0xffff ); jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 13, 0xffff ); /* Convert! */ if( header_only ) { if( read_jpeg_header( jpeg, out ) ) return( -1 ); /* Swap width and height if we're going to rotate this image. */ if( jpeg->autorotate ) { VipsAngle angle = vips_autorot_get_angle( out ); if( angle == VIPS_ANGLE_D90 || angle == VIPS_ANGLE_D270 ) VIPS_SWAP( int, out->Xsize, out->Ysize ); /* We won't be returning an orientation tag. */ (void) vips_image_remove( out, ORIENTATION ); } }
static void save_jpg_markers(struct jpeg_decompress_struct *cinfo) { /* Comment marker */ jpeg_save_markers(cinfo, JPEG_COM, JPEG_COM_MAX); /* APP0 marker = JFIF data */ jpeg_save_markers(cinfo, JPEG_APP0 + 1, 0xffff); /* APP1 marker = Exif data */ jpeg_save_markers(cinfo, JPEG_APP0 + 1, 0xffff); }
static Epeg_Image * _epeg_open_header(Epeg_Image *im) { struct jpeg_source_mgr *src_mgr = NULL; im->in.jinfo.err = jpeg_std_error(&(im->jerr.pub)); im->jerr.pub.error_exit = _epeg_fatal_error_handler; if (setjmp(im->jerr.setjmp_buffer)) { error: epeg_close(im); im = NULL; return NULL; } jpeg_create_decompress(&(im->in.jinfo)); jpeg_save_markers(&(im->in.jinfo), JPEG_APP0 + 7, 1024); jpeg_save_markers(&(im->in.jinfo), JPEG_COM, 65535); if (im->in.f != NULL) { jpeg_stdio_src(&(im->in.jinfo), im->in.f); } else { /* Setup RAM source manager. */ src_mgr = calloc(1, sizeof(struct jpeg_source_mgr)); if (!src_mgr) goto error; src_mgr->init_source = _jpeg_init_source; src_mgr->fill_input_buffer = _jpeg_fill_input_buffer; src_mgr->skip_input_data = _jpeg_skip_input_data; src_mgr->resync_to_restart = jpeg_resync_to_restart; src_mgr->term_source = _jpeg_term_source; src_mgr->bytes_in_buffer = im->in.mem.size; src_mgr->next_input_byte = (JOCTET *) im->in.mem.data; im->in.jinfo.src = (struct jpeg_source_mgr *) src_mgr; } jpeg_read_header(&(im->in.jinfo), TRUE); im->in.w = im->in.jinfo.image_width; im->in.h = im->in.jinfo.image_height; if (im->in.w < 1) goto error; if (im->in.h < 1) goto error; im->out.w = im->in.w; im->out.h = im->in.h; im->color_space = ((im->in.color_space = im->in.jinfo.out_color_space) == JCS_GRAYSCALE) ? EPEG_GRAY8 : EPEG_RGB8; if (im->in.color_space == JCS_CMYK) im->color_space = EPEG_CMYK; return im; }
jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) { #ifdef SAVE_MARKERS_SUPPORTED int m; /* Save comments except under NONE option */ if (option != JCOPYOPT_NONE) { jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); } /* Save all types of APPn markers iff ALL option */ if (option == JCOPYOPT_ALL) { for (m = 0; m < 16; m++) jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); } #endif /* SAVE_MARKERS_SUPPORTED */ }
static cmsBool OpenInput(const char* FileName) { int m; lIsITUFax = FALSE; InFile = fopen(FileName, "rb"); if (InFile == NULL) { FatalError("Cannot open '%s'", FileName); } // Now we can initialize the JPEG decompression object. Decompressor.err = jpeg_std_error(&ErrorHandler.pub); ErrorHandler.pub.error_exit = my_error_exit; ErrorHandler.pub.output_message = my_error_exit; jpeg_create_decompress(&Decompressor); jpeg_stdio_src(&Decompressor, InFile); for (m = 0; m < 16; m++) jpeg_save_markers(&Decompressor, JPEG_APP0 + m, 0xFFFF); // setup_read_icc_profile(&Decompressor); fseek(InFile, 0, SEEK_SET); jpeg_read_header(&Decompressor, TRUE); return TRUE; }
static void saveMarkers(struct jpeg_decompress_struct * const cinfoP) { unsigned int app_type; /* Get all the miscellaneous markers (COM and APPn) saved for our later access. */ jpeg_save_markers(cinfoP, JPEG_COM, 65535); for (app_type = 0; app_type <= 15; ++app_type) { if (app_type == 0 || app_type == 14) { /* The jpeg library uses APP0 and APP14 internally (see libjpeg.doc), so we don't mess with those. */ } else jpeg_save_markers(cinfoP, JPEG_APP0 + app_type, 65535); } }
/* Read a JPEG file into a VIPS image. */ int vips__jpeg_read_file( const char *filename, VipsImage *out, gboolean header_only, int shrink, gboolean fail ) { ReadJpeg *jpeg; int result; if( !(jpeg = readjpeg_new( out, shrink, fail )) ) return( -1 ); /* Here for longjmp() from vips__new_error_exit() during startup. */ if( setjmp( jpeg->eman.jmp ) ) { (void) readjpeg_free( jpeg ); return( -1 ); } /* Set input to file. */ if( readjpeg_file( jpeg, filename ) ) { (void) readjpeg_free( jpeg ); return( -1 ); } /* Need to read in APP1 (EXIF metadata), APP2 (ICC profile), APP13 * (photoshop IPCT). */ jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 1, 0xffff ); jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 2, 0xffff ); jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 13, 0xffff ); /* Convert! */ if( header_only ) result = read_jpeg_header( jpeg, out ); else result = read_jpeg_image( jpeg, out ); /* Don't call readjpeg_free(), we're probably still live. */ return( result ); }
/** * im_bufjpeg2vips: * @buf: memory area to load * @len: size of memory area * @out: image to write * @header_only: set to just read the header * * Read a JPEG-formatted memory block into a VIPS image. It can read most * 8-bit JPEG images, including CMYK and YCbCr. * * This function is handy for processing JPEG image thumbnails. * * See also: #VipsFormat, im_jpeg2vips(). * * Returns: 0 on success, -1 on error. */ int im_bufjpeg2vips( void *buf, size_t len, IMAGE *out, gboolean header_only ) { struct jpeg_decompress_struct cinfo; ErrorManager eman; int result; gboolean invert_pels; /* Make jpeg dcompression object. */ cinfo.err = jpeg_std_error( &eman.pub ); eman.pub.error_exit = new_error_exit; eman.pub.output_message = new_output_message; eman.fp = NULL; if( setjmp( eman.jmp ) ) { /* Here for longjmp() from new_error_exit(). */ jpeg_destroy_decompress( &cinfo ); return( -1 ); } jpeg_create_decompress( &cinfo ); /* Make input. */ buf_source( &cinfo, buf, len ); /* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile). */ jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff ); jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff ); /* Convert! */ result = read_jpeg_header( &cinfo, out, &invert_pels, 1 ); if( !header_only && !result ) result = read_jpeg_image( &cinfo, out, invert_pels ); /* Close and tidy. */ jpeg_destroy_decompress( &cinfo ); return( result ); }
static FX_BOOL _JpegLoadInfo(FX_LPCBYTE src_buf, FX_DWORD src_size, int& width, int& height, int& num_components, int& bits_per_components, FX_BOOL& color_transform, FX_LPBYTE* icc_buf_ptr, FX_DWORD* icc_length) { _JpegScanSOI(src_buf, src_size); struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; jerr.error_exit = _error_fatal; jerr.emit_message = _error_do_nothing1; jerr.output_message = _error_do_nothing; jerr.format_message = _error_do_nothing2; jerr.reset_error_mgr = _error_do_nothing; jerr.trace_level = 0; cinfo.err = &jerr; jmp_buf mark; cinfo.client_data = &mark; if (setjmp(mark) == -1) { return FALSE; } jpeg_create_decompress(&cinfo); struct jpeg_source_mgr src; src.init_source = _src_do_nothing; src.term_source = _src_do_nothing; src.skip_input_data = _src_skip_data; src.fill_input_buffer = _src_fill_buffer; src.resync_to_restart = _src_resync; src.bytes_in_buffer = src_size; src.next_input_byte = src_buf; cinfo.src = &src; if (setjmp(mark) == -1) { jpeg_destroy_decompress(&cinfo); return FALSE; } if(icc_buf_ptr && icc_length) { jpeg_save_markers(&cinfo, JPEG_MARKER_ICC, JPEG_MARKER_MAXSIZE); } int ret = jpeg_read_header(&cinfo, TRUE); if (ret != JPEG_HEADER_OK) { jpeg_destroy_decompress(&cinfo); return FALSE; } width = cinfo.image_width; height = cinfo.image_height; num_components = cinfo.num_components; color_transform = cinfo.jpeg_color_space == JCS_YCbCr || cinfo.jpeg_color_space == JCS_YCCK; bits_per_components = cinfo.data_precision; if(icc_buf_ptr != NULL) { *icc_buf_ptr = NULL; } if(icc_length != NULL) { *icc_length = 0; } jpeg_destroy_decompress(&cinfo); return TRUE; }
int vips__jpeg_read_buffer( void *buf, size_t len, VipsImage *out, gboolean header_only, int shrink, int fail ) { ReadJpeg *jpeg; int result; if( !(jpeg = readjpeg_new( out, shrink, fail )) ) return( -1 ); if( setjmp( jpeg->eman.jmp ) ) { (void) readjpeg_free( jpeg ); return( -1 ); } /* Set input to buffer. */ readjpeg_buffer( jpeg, buf, len ); /* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile). */ jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 1, 0xffff ); jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 2, 0xffff ); /* Convert! */ if( header_only ) result = read_jpeg_header( jpeg, out ); else result = read_jpeg_image( jpeg, out ); /* Don't call readjpeg_free(), we're probably still live. */ return( result ); }
void ScImgDataLoader_JPEG::loadEmbeddedProfile(const QString& fn, int /*page*/) { m_embeddedProfile.resize(0); m_profileComponents = 0; if (!QFile::exists(fn)) return; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; infile = NULL; if (setjmp (jerr.setjmp_buffer)) { jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); return; } jpeg_create_decompress (&cinfo); if ((infile = fopen (fn.toLocal8Bit(), "rb")) == NULL) return; jpeg_stdio_src(&cinfo, infile); jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF); jpeg_read_header(&cinfo, true); //jpeg_start_decompress(&cinfo); unsigned int EmbedLen = 0; unsigned char* EmbedBuffer; if (read_jpeg_marker(ICC_MARKER,&cinfo, &EmbedBuffer, &EmbedLen)) { cmsHPROFILE prof = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen); if (prof) { if (static_cast<int>(cmsGetColorSpace(prof)) == icSigRgbData) m_profileComponents = 3; if (static_cast<int>(cmsGetColorSpace(prof)) == icSigCmykData) m_profileComponents = 4; if (static_cast<int>(cmsGetColorSpace(prof)) == icSigGrayData) m_profileComponents = 1; m_embeddedProfile = QByteArray((const char*) EmbedBuffer, EmbedLen); } cmsCloseProfile(prof); free(EmbedBuffer); } //(void) jpeg_finish_decompress(&cinfo); fclose (infile); jpeg_destroy_decompress (&cinfo); }
void ScImgDataLoader_JPEG::loadEmbeddedProfile(const QString& fn, int /*page*/) { m_embeddedProfile.resize(0); m_profileComponents = 0; if (!QFile::exists(fn)) return; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; infile = NULL; if (setjmp (jerr.setjmp_buffer)) { jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); return; } jpeg_create_decompress (&cinfo); if ((infile = fopen (fn.toLocal8Bit(), "rb")) == NULL) return; jpeg_stdio_src(&cinfo, infile); jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF); jpeg_read_header(&cinfo, true); unsigned int EmbedLen = 0; unsigned char* EmbedBuffer; if (read_jpeg_marker(ICC_MARKER,&cinfo, &EmbedBuffer, &EmbedLen)) { QByteArray profArray = QByteArray((const char*) EmbedBuffer, EmbedLen); ScColorProfile prof = ScColorMgmtEngine::openProfileFromMem(profArray); if (prof) { if (prof.colorSpace() == ColorSpace_Rgb) m_profileComponents = 3; if (prof.colorSpace() == ColorSpace_Cmyk) m_profileComponents = 4; if (prof.colorSpace() == ColorSpace_Gray) m_profileComponents = 1; m_embeddedProfile = profArray; } free(EmbedBuffer); } fclose (infile); jpeg_destroy_decompress (&cinfo); }
JPEGImageReader(JPEGImageDecoder* decoder) : m_decoder(decoder) , m_bufferLength(0) , m_bytesToSkip(0) , m_state(JPEG_HEADER) , m_samples(0) #if USE(QCMSLIB) , m_transform(0) #endif { memset(&m_info, 0, sizeof(jpeg_decompress_struct)); // We set up the normal JPEG error routines, then override error_exit. m_info.err = jpeg_std_error(&m_err.pub); m_err.pub.error_exit = error_exit; // Allocate and initialize JPEG decompression object. jpeg_create_decompress(&m_info); decoder_source_mgr* src = 0; if (!m_info.src) { src = (decoder_source_mgr*)fastZeroedMalloc(sizeof(decoder_source_mgr)); if (!src) { m_state = JPEG_ERROR; return; } } m_info.src = (jpeg_source_mgr*)src; // Set up callback functions. src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; src->pub.term_source = term_source; src->decoder = this; #if USE(ICCJPEG) // Retain ICC color profile markers for color management. setup_read_icc_profile(&m_info); #endif // Keep APP1 blocks, for obtaining exif data. jpeg_save_markers(&m_info, exifMarker, 0xFFFF); }
JPEGImageReader(JPEGImageDecoder* decoder) : m_decoder(decoder) , m_bufferLength(0) , m_bytesToSkip(0) , m_state(JPEG_HEADER) , m_samples(0) { memset(&m_info, 0, sizeof(jpeg_decompress_struct)); // We set up the normal JPEG error routines, then override error_exit. m_info.err = jpeg_std_error(&m_err.pub); m_err.pub.error_exit = error_exit; // Allocate and initialize JPEG decompression object. jpeg_create_decompress(&m_info); decoder_source_mgr* src = 0; if (!m_info.src) { src = (decoder_source_mgr*)fastCalloc(sizeof(decoder_source_mgr), 1); if (!src) { m_state = JPEG_ERROR; return; } } m_info.src = (jpeg_source_mgr*)src; // Set up callback functions. src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; src->pub.term_source = term_source; src->decoder = this; // Enable these markers for the ICC color profile. // Apparently there are 16 of these markers. I don't see anywhere in the header with this constant. for (unsigned i = 0; i < 0xF; ++i) jpeg_save_markers(&m_info, JPEG_APP0 + i, 0xFFFF); }
void nsJPEGDecoder::InitInternal() { mCMSMode = gfxPlatform::GetCMSMode(); if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) mCMSMode = eCMSMode_Off; /* We set up the normal JPEG error routines, then override error_exit. */ mInfo.err = jpeg_std_error(&mErr.pub); /* mInfo.err = jpeg_std_error(&mErr.pub); */ mErr.pub.error_exit = my_error_exit; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp(mErr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ PostDecoderError(NS_ERROR_FAILURE); return; } /* Step 1: allocate and initialize JPEG decompression object */ jpeg_create_decompress(&mInfo); /* Set the source manager */ mInfo.src = &mSourceMgr; /* Step 2: specify data source (eg, a file) */ /* Setup callback functions. */ mSourceMgr.init_source = init_source; mSourceMgr.fill_input_buffer = fill_input_buffer; mSourceMgr.skip_input_data = skip_input_data; mSourceMgr.resync_to_restart = jpeg_resync_to_restart; mSourceMgr.term_source = term_source; /* Record app markers for ICC data */ for (uint32_t m = 0; m < 16; m++) jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF); }
void setup_read_icc_profile (j_decompress_ptr cinfo) { /* Tell the library to keep any APP2 data it may find */ jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF); }
bool JpgInput::open (const std::string &name, ImageSpec &newspec) { // Check that file exists and can be opened m_filename = name; m_fd = Filesystem::fopen (name, "rb"); if (m_fd == NULL) { error ("Could not open file \"%s\"", name.c_str()); return false; } // Check magic number to assure this is a JPEG file uint8_t magic[2] = {0, 0}; if (fread (magic, sizeof(magic), 1, m_fd) != 1) { error ("Empty file \"%s\"", name.c_str()); close_file (); return false; } rewind (m_fd); if (magic[0] != JPEG_MAGIC1 || magic[1] != JPEG_MAGIC2) { close_file (); error ("\"%s\" is not a JPEG file, magic number doesn't match (was 0x%x%x)", name.c_str(), int(magic[0]), int(magic[1])); return false; } // Set up the normal JPEG error routines, then override error_exit and // output_message so we intercept all the errors. m_cinfo.err = jpeg_std_error ((jpeg_error_mgr *)&m_jerr); m_jerr.pub.error_exit = my_error_exit; m_jerr.pub.output_message = my_output_message; if (setjmp (m_jerr.setjmp_buffer)) { // Jump to here if there's a libjpeg internal error // Prevent memory leaks, see example.c in jpeg distribution jpeg_destroy_decompress (&m_cinfo); close_file (); return false; } jpeg_create_decompress (&m_cinfo); // initialize decompressor jpeg_stdio_src (&m_cinfo, m_fd); // specify the data source // Request saving of EXIF and other special tags for later spelunking for (int mark = 0; mark < 16; ++mark) jpeg_save_markers (&m_cinfo, JPEG_APP0+mark, 0xffff); jpeg_save_markers (&m_cinfo, JPEG_COM, 0xffff); // comment marker // read the file parameters if (jpeg_read_header (&m_cinfo, FALSE) != JPEG_HEADER_OK || m_fatalerr) { error ("Bad JPEG header for \"%s\"", filename().c_str()); return false; } int nchannels = m_cinfo.num_components; if (m_cinfo.jpeg_color_space == JCS_CMYK || m_cinfo.jpeg_color_space == JCS_YCCK) { // CMYK jpegs get converted by us to RGB m_cinfo.out_color_space = JCS_CMYK; // pre-convert YCbCrK->CMYK nchannels = 3; m_cmyk = true; } if (m_raw) m_coeffs = jpeg_read_coefficients (&m_cinfo); else jpeg_start_decompress (&m_cinfo); // start working if (m_fatalerr) return false; m_next_scanline = 0; // next scanline we'll read m_spec = ImageSpec (m_cinfo.output_width, m_cinfo.output_height, nchannels, TypeDesc::UINT8); // Assume JPEG is in sRGB unless the Exif or XMP tags say otherwise. m_spec.attribute ("oiio:ColorSpace", "sRGB"); if (m_cinfo.jpeg_color_space == JCS_CMYK) m_spec.attribute ("jpeg:ColorSpace", "CMYK"); else if (m_cinfo.jpeg_color_space == JCS_YCCK) m_spec.attribute ("jpeg:ColorSpace", "YCbCrK"); // If the chroma subsampling is detected and matches something // we expect, then set an attribute so that it can be preserved // in future operations. std::string subsampling = comp_info_to_attr(m_cinfo); if (!subsampling.empty()) m_spec.attribute(JPEG_SUBSAMPLING_ATTR, subsampling); for (jpeg_saved_marker_ptr m = m_cinfo.marker_list; m; m = m->next) { if (m->marker == (JPEG_APP0+1) && ! strcmp ((const char *)m->data, "Exif")) { // The block starts with "Exif\0\0", so skip 6 bytes to get // to the start of the actual Exif data TIFF directory decode_exif ((unsigned char *)m->data+6, m->data_length-6, m_spec); } else if (m->marker == (JPEG_APP0+1) && ! strcmp ((const char *)m->data, "http://ns.adobe.com/xap/1.0/")) { #ifndef NDEBUG std::cerr << "Found APP1 XMP! length " << m->data_length << "\n"; #endif std::string xml ((const char *)m->data, m->data_length); decode_xmp (xml, m_spec); } else if (m->marker == (JPEG_APP0+13) && ! strcmp ((const char *)m->data, "Photoshop 3.0")) jpeg_decode_iptc ((unsigned char *)m->data); else if (m->marker == JPEG_COM) { if (! m_spec.find_attribute ("ImageDescription", TypeDesc::STRING)) m_spec.attribute ("ImageDescription", std::string ((const char *)m->data, m->data_length)); } } // Handle density/pixelaspect. We need to do this AFTER the exif is // decoded, in case it contains useful information. float xdensity = m_spec.get_float_attribute ("XResolution"); float ydensity = m_spec.get_float_attribute ("YResolution"); if (! xdensity || ! ydensity) { xdensity = float(m_cinfo.X_density); ydensity = float(m_cinfo.Y_density); if (xdensity && ydensity) { m_spec.attribute ("XResolution", xdensity); m_spec.attribute ("YResolution", ydensity); } } if (xdensity && ydensity) { float aspect = ydensity/xdensity; if (aspect != 1.0f) m_spec.attribute ("PixelAspectRatio", aspect); switch (m_cinfo.density_unit) { case 0 : m_spec.attribute ("ResolutionUnit", "none"); break; case 1 : m_spec.attribute ("ResolutionUnit", "in"); break; case 2 : m_spec.attribute ("ResolutionUnit", "cm"); break; } } read_icc_profile(&m_cinfo, m_spec); /// try to read icc profile newspec = m_spec; return true; }
void Jpegoptim(sLONG_PTR *pResult, PackagePtr pParams) { C_LONGINT Param2_Options; C_LONGINT Param3_Quality; int quality = -1; int save_exif = 0; int save_iptc = 0; int save_com = 0; int save_icc = 0; int save_xmp = 0; struct jpeg_decompress_struct dinfo; struct jpeg_compress_struct cinfo; struct my_error_mgr jcerr, jderr; Param2_Options.fromParamAtIndex(pParams, 2); unsigned int o = Param2_Options.getIntValue(); if(o) { save_exif = !(o & JPEG_STRIP_EXIF); save_iptc = !(o & JPEG_STRIP_IPTC); save_com = !(o & JPEG_STRIP_COM ); save_icc = !(o & JPEG_STRIP_ICC ); save_xmp = !(o & JPEG_STRIP_XMP ); } Param3_Quality.fromParamAtIndex(pParams, 3); unsigned int q = Param3_Quality.getIntValue(); if ((q >= 1) && (q <= 101)) { quality = (q-1); } jvirt_barray_ptr *coef_arrays = NULL; JSAMPARRAY buf = NULL; /* get jpeg data */ std::vector<unsigned char>pictureData; std::string type(".jpeg"); if(getPictureDataForType(pParams, 1, pictureData, type)) { /* initialize decompression object */ dinfo.err = jpeg_std_error(&jderr.pub); jpeg_create_decompress(&dinfo); jderr.pub.error_exit=my_error_exit; jderr.pub.output_message=my_output_message; jderr.jump_set = 0; /* initialize compression object */ cinfo.err = jpeg_std_error(&jcerr.pub); jpeg_create_compress(&cinfo); jcerr.pub.error_exit=my_error_exit; jcerr.pub.output_message=my_output_message; jcerr.jump_set = 0; if (setjmp(jderr.setjmp_buffer)) { /* error handler for decompress */ jpeg_abort_decompress(&dinfo); jderr.jump_set=0; } else { jderr.jump_set=1; } /* prepare to decompress */ jpeg_save_markers(&dinfo, JPEG_COM, 0xffff); for (int j=0;j<=15;j++) jpeg_save_markers(&dinfo, JPEG_APP0+j, 0xffff); jpeg_mem_src(&dinfo, &pictureData[0], pictureData.size()); jpeg_read_header(&dinfo, TRUE); jpeg_start_decompress(&dinfo); if(quality == -1) { coef_arrays = jpeg_read_coefficients(&dinfo); }else { buf = (JSAMPARRAY)malloc(sizeof(JSAMPROW)*dinfo.output_height); for (int j=0;j<dinfo.output_height;j++) { buf[j]=(JSAMPROW)malloc(sizeof(JSAMPLE)*dinfo.output_width*dinfo.out_color_components); } while (dinfo.output_scanline < dinfo.output_height) { PA_YieldAbsolute(); jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline], dinfo.output_height-dinfo.output_scanline); } } if (setjmp(jcerr.setjmp_buffer)) { /* error handler for compress failures */ jpeg_abort_compress(&cinfo); jpeg_abort_decompress(&dinfo); jcerr.jump_set=0; } else { jcerr.jump_set=1; } size_t outbuffersize = pictureData.size() + 32768; unsigned char *outbuffer = (unsigned char *)malloc(outbuffersize); if(outbuffer) { jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536); if(quality == -1) { jpeg_copy_critical_parameters(&dinfo, &cinfo); jpeg_simple_progression(&cinfo); cinfo.optimize_coding = TRUE; }else { cinfo.in_color_space=dinfo.out_color_space; cinfo.input_components=dinfo.output_components; cinfo.image_width=dinfo.image_width; cinfo.image_height=dinfo.image_height; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo,quality,TRUE); jpeg_simple_progression(&cinfo); cinfo.optimize_coding = TRUE; jpeg_start_compress(&cinfo,TRUE); } write_markers(&dinfo,&cinfo, save_exif, save_iptc, save_com, save_icc, save_xmp); if(quality == -1) { jpeg_write_coefficients(&cinfo, coef_arrays); }else { while (cinfo.next_scanline < cinfo.image_height) { PA_YieldAbsolute(); jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], dinfo.output_height); } } jpeg_finish_decompress(&dinfo); if(quality != -1) { jpeg_finish_compress(&cinfo); FREE_LINE_BUF(buf,dinfo.output_height); } jpeg_destroy_decompress(&dinfo); jpeg_destroy_compress(&cinfo); PA_Picture picture = PA_CreatePicture((void *)outbuffer, outbuffersize); *(PA_Picture*) pResult = picture; free(outbuffer); } } }
unsigned char* simage_jpeg_load(std::istream& fin, int *width_ret, int *height_ret, int *numComponents_ret, unsigned int* exif_orientation) { int width; int height; unsigned char *currPtr; int format; /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). */ struct jpeg_decompress_struct cinfo; /* We use our private extension JPEG error handler. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ struct my_error_mgr jerr; /* More stuff */ //FILE * infile; /* source file */ JSAMPARRAY rowbuffer; /* Output row buffer */ int row_stride; /* physical row width in output buffer */ jpegerror = ERR_NO_ERROR; /* In this example we want to open the input file before doing anything else, * so that the setjmp() error recovery below can assume the file is open. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to read binary files. */ /*if ((infile = fopen(filename, "rb")) == NULL) { jpegerror = ERR_OPEN; return NULL; }*/ /* Step 1: allocate and initialize JPEG decompression object */ /* We set up the normal JPEG error routines, then override error_exit. */ cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; jerr.pub.output_message = my_output_message; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp(jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpegerror = ERR_JPEGLIB; jpeg_destroy_decompress(&cinfo); //fclose(infile); //if (buffer) delete [] buffer; return NULL; } // used to be before setjump above, but have moved to after to avoid compile warnings. unsigned char *buffer = NULL; /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress(&cinfo); /* Step 2: specify data source (eg, a file) */ //jpeg_stdio_src(&cinfo, infile); jpeg_istream_src(&cinfo,&fin); /* Step 3: read file parameters with jpeg_read_header() */ jpeg_save_markers (&cinfo, EXIF_JPEG_MARKER, 0xffff); (void) jpeg_read_header(&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* check for orientation tag */ *exif_orientation = EXIF_Orientation (&cinfo); if (*exif_orientation!=0) { OSG_INFO<<"We have an EXIF_Orientation "<<exif_orientation<<std::endl; } /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ if (cinfo.jpeg_color_space == JCS_GRAYSCALE) { format = 1; cinfo.out_color_space = JCS_GRAYSCALE; } else /* use rgb */ { format = 3; cinfo.out_color_space = JCS_RGB; } (void) jpeg_start_decompress(&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* JSAMPLEs per row in output buffer */ row_stride = cinfo.output_width * cinfo.output_components; /* Make a one-row-high sample array that will go away when done with image */ rowbuffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); width = cinfo.output_width; height = cinfo.output_height; buffer = currPtr = new unsigned char [width*height*cinfo.output_components]; /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ /* flip image upside down */ if (buffer) { currPtr = buffer + row_stride * (cinfo.output_height-1); while (cinfo.output_scanline < cinfo.output_height) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ (void) jpeg_read_scanlines(&cinfo, rowbuffer, 1); /* Assume put_scanline_someplace wants a pointer and sample count. */ currPtr = copyScanline(currPtr, rowbuffer[0], row_stride); } } /* Step 7: Finish decompression */ (void) jpeg_finish_decompress(&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress(&cinfo); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ //fclose(infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.pub.num_warnings is nonzero). */ /* And we're done! */ if (buffer) { *width_ret = width; *height_ret = height; *numComponents_ret = format; } else { jpegerror = ERR_MEM; } return buffer; }
static Epeg_Image * _epeg_open_header(Epeg_Image *im) { struct jpeg_marker_struct *m; struct jpeg_source_mgr *src_mgr = NULL; im->in.jinfo.err = jpeg_std_error(&(im->jerr.pub)); im->jerr.pub.error_exit = _epeg_fatal_error_handler; #ifdef NOWARNINGS im->jerr.pub.emit_message = _emit_message; im->jerr.pub.output_message = _output_message; im->jerr.pub.format_message = _format_message; #endif if (setjmp(im->jerr.setjmp_buffer)) { error: epeg_close(im); im = NULL; return NULL; } jpeg_create_decompress(&(im->in.jinfo)); jpeg_save_markers(&(im->in.jinfo), JPEG_APP0 + 7, 1024); jpeg_save_markers(&(im->in.jinfo), JPEG_COM, 65535); if (im->in.f != NULL) { jpeg_stdio_src(&(im->in.jinfo), im->in.f); } else { /* Setup RAM source manager. */ src_mgr = calloc(1, sizeof(struct jpeg_source_mgr)); if (!src_mgr) { goto error; } src_mgr->init_source = _jpeg_init_source; src_mgr->fill_input_buffer = _jpeg_fill_input_buffer; src_mgr->skip_input_data = _jpeg_skip_input_data; src_mgr->resync_to_restart = jpeg_resync_to_restart; src_mgr->term_source = _jpeg_term_source; src_mgr->bytes_in_buffer = im->in.mem.size; src_mgr->next_input_byte = (JOCTET *) im->in.mem.data; im->in.jinfo.src = (struct jpeg_source_mgr *) src_mgr; } jpeg_read_header(&(im->in.jinfo), TRUE); im->in.w = im->in.jinfo.image_width; im->in.h = im->in.jinfo.image_height; if (im->in.w < 1) { goto error; } if (im->in.h < 1) { goto error; } im->out.w = im->in.w; im->out.h = im->in.h; im->color_space = ((im->in.color_space = im->in.jinfo.out_color_space) == JCS_GRAYSCALE) ? EPEG_GRAY8 : EPEG_RGB8; if (im->in.color_space == JCS_CMYK) { im->color_space = EPEG_CMYK; } for (m = im->in.jinfo.marker_list; m; m = m->next) { if (m->marker == JPEG_COM) { if (im->in.comment) { free(im->in.comment); } im->in.comment = malloc(m->data_length + 1); if (im->in.comment) { memcpy(im->in.comment, m->data, m->data_length); im->in.comment[m->data_length] = 0; } } else if (m->marker == (JPEG_APP0 + 7)) { if ((m->data_length > 7) && (!strncmp((char *)m->data, "Thumb::", 7))) { char *p, *p2; p = malloc(m->data_length + 1); if (p) { memcpy(p, m->data, m->data_length); p[m->data_length] = 0; p2 = strchr(p, '\n'); if (p2) { p2[0] = 0; if (!strcmp(p, "Thumb::URI")) { im->in.thumb_info.uri = strdup(p2 + 1); } else if (!strcmp(p, "Thumb::MTime")) { sscanf(p2 + 1, "%llu", &(im->in.thumb_info.mtime)); } else if (!strcmp(p, "Thumb::Image::Width")) { im->in.thumb_info.w = atoi(p2 + 1); } else if (!strcmp(p, "Thumb::Image::Height")) { im->in.thumb_info.h = atoi(p2 + 1); } else if (!strcmp(p, "Thumb::Mimetype")) { im->in.thumb_info.mime = strdup(p2 + 1); } } free(p); } } } } return im; }
// returns w and h and tw and th and comment as a convenience static bool verify_jpeg(FILE *f, int32_t *w, int32_t *h, int32_t *tw, int32_t *th, char **comment, GError **err) { struct jpeg_decompress_struct cinfo; struct _openslide_jpeg_error_mgr jerr; jmp_buf env; bool success = false; *w = 0; *h = 0; *tw = 0; *th = 0; if (comment) { *comment = NULL; } if (setjmp(env) == 0) { cinfo.err = _openslide_jpeg_set_error_handler(&jerr, &env); jpeg_create_decompress(&cinfo); _openslide_jpeg_stdio_src(&cinfo, f); int header_result; if (comment) { // extract comment jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF); } header_result = jpeg_read_header(&cinfo, TRUE); if (header_result != JPEG_HEADER_OK && header_result != JPEG_HEADER_TABLES_ONLY) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Couldn't read JPEG header"); goto DONE; } if (cinfo.num_components != 3) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "JPEG color components != 3"); goto DONE; } if (cinfo.restart_interval == 0) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "No restart markers"); goto DONE; } jpeg_start_decompress(&cinfo); if (comment) { if (cinfo.marker_list) { // copy everything out char *com = g_strndup((const gchar *) cinfo.marker_list->data, cinfo.marker_list->data_length); // but only really save everything up to the first '\0' *comment = g_strdup(com); g_free(com); } jpeg_save_markers(&cinfo, JPEG_COM, 0); // stop saving } *w = cinfo.output_width; *h = cinfo.output_height; if (cinfo.restart_interval > cinfo.MCUs_per_row) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Restart interval greater than MCUs per row"); goto DONE; } *tw = *w / (cinfo.MCUs_per_row / cinfo.restart_interval); *th = *h / cinfo.MCU_rows_in_scan; int leftover_mcus = cinfo.MCUs_per_row % cinfo.restart_interval; if (leftover_mcus != 0) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Inconsistent restart marker spacing within row"); goto DONE; } // g_debug("w: %d, h: %d, restart_interval: %d\n" // "mcus_per_row: %d, mcu_rows_in_scan: %d\n" // "leftover mcus: %d", // cinfo.output_width, cinfo.output_height, // cinfo.restart_interval, // cinfo.MCUs_per_row, cinfo.MCU_rows_in_scan, // leftover_mcus); } else { // setjmp has returned again g_propagate_error(err, jerr.err); goto DONE; } success = true; DONE: jpeg_destroy_decompress(&cinfo); return success; }
int imFileFormatJPEG::ReadImageInfo(int index) { (void)index; this->fix_adobe_cmyk = 0; if (setjmp(this->jerr.setjmp_buffer)) return IM_ERR_ACCESS; // notify libjpeg to save the COM marker jpeg_save_markers(&this->dinfo, JPEG_COM, 0xFFFF); jpeg_save_markers(&this->dinfo, JPEG_APP0+1, 0xFFFF); /* Step 3: read file parameters with jpeg_read_header() */ if (jpeg_read_header(&this->dinfo, TRUE) != JPEG_HEADER_OK) return IM_ERR_ACCESS; this->width = this->dinfo.image_width; this->height = this->dinfo.image_height; this->file_data_type = IM_BYTE; switch(this->dinfo.jpeg_color_space) { case JCS_GRAYSCALE: this->file_color_mode = IM_GRAY; break; case JCS_RGB: this->file_color_mode = IM_RGB; break; case JCS_YCbCr: this->file_color_mode = IM_RGB; break; case JCS_CMYK: this->file_color_mode = IM_CMYK; if (this->dinfo.saw_Adobe_marker) this->fix_adobe_cmyk = 1; break; case JCS_YCCK: this->file_color_mode = IM_CMYK; this->dinfo.out_color_space = JCS_CMYK; // this is the only supported conversion in libjpeg if (this->dinfo.saw_Adobe_marker) this->fix_adobe_cmyk = 1; break; default: /* JCS_UNKNOWN */ return IM_ERR_DATA; } imAttribTable* attrib_table = AttribTable(); int* auto_ycbcr = (int*)attrib_table->Get("AutoYCbCr"); if (auto_ycbcr && *auto_ycbcr == 0 && this->dinfo.jpeg_color_space == JCS_YCbCr) { this->file_color_mode = IM_YCBCR; this->dinfo.out_color_space = JCS_YCbCr; } this->file_color_mode |= IM_TOPDOWN; if (imColorModeDepth(this->file_color_mode) > 1) this->file_color_mode |= IM_PACKED; if (this->dinfo.progressive_mode != 0) { int progressive = 1; attrib_table->Set("Interlaced", IM_INT, 1, &progressive); } if (this->dinfo.density_unit != 0) { float xres = (float)this->dinfo.X_density, yres = (float)this->dinfo.Y_density; if (this->dinfo.density_unit == 1) attrib_table->Set("ResolutionUnit", IM_BYTE, -1, "DPI"); else attrib_table->Set("ResolutionUnit", IM_BYTE, -1, "DPC"); attrib_table->Set("XResolution", IM_FLOAT, 1, (void*)&xres); attrib_table->Set("YResolution", IM_FLOAT, 1, (void*)&yres); } if (this->dinfo.marker_list) { jpeg_saved_marker_ptr cur_marker = this->dinfo.marker_list; // search for COM marker while (cur_marker) { if (cur_marker->marker == JPEG_COM) { char* desc = new char [cur_marker->data_length+1]; memcpy(desc, cur_marker->data, cur_marker->data_length); desc[cur_marker->data_length] = 0; attrib_table->Set("Description", IM_BYTE, cur_marker->data_length+1, desc); delete [] desc; } #ifdef USE_EXIF if (cur_marker->marker == JPEG_APP0+1) iReadExifAttrib(cur_marker->data, cur_marker->data_length, attrib_table); #endif cur_marker = cur_marker->next; } } /* Step 5: Start decompressor */ if (jpeg_start_decompress(&this->dinfo) == FALSE) return IM_ERR_ACCESS; return IM_ERR_NONE; }
/* * context - from image_begin_load * buf - new image data * size - length of new image data * * append image data onto inrecrementally built output image */ static gboolean gdk_pixbuf__jpeg_image_load_increment (gpointer data, const guchar *buf, guint size, GError **error) { JpegProgContext *context = (JpegProgContext *)data; struct jpeg_decompress_struct *cinfo; my_src_ptr src; guint num_left, num_copy; guint last_num_left, last_bytes_left; guint spinguard; gboolean first; const guchar *bufhd; gint width, height; int is_otag; char otag_str[5]; g_return_val_if_fail (context != NULL, FALSE); g_return_val_if_fail (buf != NULL, FALSE); src = (my_src_ptr) context->cinfo.src; cinfo = &context->cinfo; context->jerr.error = error; /* check for fatal error */ if (sigsetjmp (context->jerr.setjmp_buffer, 1)) { return FALSE; } /* skip over data if requested, handle unsigned int sizes cleanly */ /* only can happen if we've already called jpeg_get_header once */ if (context->src_initialized && src->skip_next) { if (src->skip_next > size) { src->skip_next -= size; return TRUE; } else { num_left = size - src->skip_next; bufhd = buf + src->skip_next; src->skip_next = 0; } } else { num_left = size; bufhd = buf; } if (num_left == 0) return TRUE; last_num_left = num_left; last_bytes_left = 0; spinguard = 0; first = TRUE; while (TRUE) { /* handle any data from caller we haven't processed yet */ if (num_left > 0) { if(src->pub.bytes_in_buffer && src->pub.next_input_byte != src->buffer) memmove(src->buffer, src->pub.next_input_byte, src->pub.bytes_in_buffer); num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer, num_left); memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy); src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer += num_copy; bufhd += num_copy; num_left -= num_copy; } /* did anything change from last pass, if not return */ if (first) { last_bytes_left = src->pub.bytes_in_buffer; first = FALSE; } else if (src->pub.bytes_in_buffer == last_bytes_left && num_left == last_num_left) { spinguard++; } else { last_bytes_left = src->pub.bytes_in_buffer; last_num_left = num_left; } /* should not go through twice and not pull bytes out of buf */ if (spinguard > 2) return TRUE; /* try to load jpeg header */ if (!context->got_header) { int rc; jpeg_save_markers (cinfo, EXIF_JPEG_MARKER, 0xffff); rc = jpeg_read_header (cinfo, TRUE); context->src_initialized = TRUE; if (rc == JPEG_SUSPENDED) continue; context->got_header = TRUE; /* check for orientation tag */ is_otag = get_orientation (cinfo); width = cinfo->image_width; height = cinfo->image_height; if (context->size_func) { (* context->size_func) (&width, &height, context->user_data); if (width == 0 || height == 0) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Transformed JPEG has zero width or height.")); return FALSE; } } cinfo->scale_num = 1; for (cinfo->scale_denom = 2; cinfo->scale_denom <= 8; cinfo->scale_denom *= 2) { jpeg_calc_output_dimensions (cinfo); if (cinfo->output_width < width || cinfo->output_height < height) { cinfo->scale_denom /= 2; break; } } jpeg_calc_output_dimensions (cinfo); context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, cinfo->output_components == 4 ? TRUE : FALSE, 8, cinfo->output_width, cinfo->output_height); if (context->pixbuf == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for loading JPEG file")); return FALSE; } /* if orientation tag was found set an option to remember its value */ if (is_otag) { g_snprintf (otag_str, sizeof (otag_str), "%d", is_otag); gdk_pixbuf_set_option (context->pixbuf, "orientation", otag_str); } /* Use pixbuf buffer to store decompressed data */ context->dptr = context->pixbuf->pixels; /* Notify the client that we are ready to go */ if (context->prepared_func) (* context->prepared_func) (context->pixbuf, NULL, context->user_data); } else if (!context->did_prescan) { int rc; /* start decompression */ cinfo->buffered_image = cinfo->progressive_mode; rc = jpeg_start_decompress (cinfo); cinfo->do_fancy_upsampling = FALSE; cinfo->do_block_smoothing = FALSE; if (rc == JPEG_SUSPENDED) continue; context->did_prescan = TRUE; } else if (!cinfo->buffered_image) { /* we're decompressing unbuffered so * simply get scanline by scanline from jpeg lib */ if (! gdk_pixbuf__jpeg_image_load_lines (context, error)) return FALSE; if (cinfo->output_scanline >= cinfo->output_height) return TRUE; } else { /* we're decompressing buffered (progressive) * so feed jpeg lib scanlines */ /* keep going until we've done all passes */ while (!jpeg_input_complete (cinfo)) { if (!context->in_output) { if (jpeg_start_output (cinfo, cinfo->input_scan_number)) { context->in_output = TRUE; context->dptr = context->pixbuf->pixels; } else break; } /* get scanlines from jpeg lib */ if (! gdk_pixbuf__jpeg_image_load_lines (context, error)) return FALSE; if (cinfo->output_scanline >= cinfo->output_height && jpeg_finish_output (cinfo)) context->in_output = FALSE; else break; } if (jpeg_input_complete (cinfo)) /* did entire image */ return TRUE; else continue; } } }
/* Shared library entry point */ static GdkPixbuf * gdk_pixbuf__jpeg_image_load (FILE *f, GError **error) { gint i; int is_otag; char otag_str[5]; GdkPixbuf * volatile pixbuf = NULL; guchar *dptr; guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, * from the header file: * " Usually rec_outbuf_height will be 1 or 2, * at most 4." */ guchar **lptr; struct jpeg_decompress_struct cinfo; struct error_handler_data jerr; stdio_src_ptr src; /* setup error handler */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = fatal_error_handler; jerr.pub.output_message = output_message_handler; jerr.error = error; if (sigsetjmp (jerr.setjmp_buffer, 1)) { /* Whoops there was a jpeg error */ if (pixbuf) g_object_unref (pixbuf); jpeg_destroy_decompress (&cinfo); /* error should have been set by fatal_error_handler () */ return NULL; } /* load header, setup */ jpeg_create_decompress (&cinfo); cinfo.src = (struct jpeg_source_mgr *) (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, sizeof (stdio_source_mgr)); src = (stdio_src_ptr) cinfo.src; src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, JPEG_PROG_BUF_SIZE * sizeof (JOCTET)); src->pub.init_source = stdio_init_source; src->pub.fill_input_buffer = stdio_fill_input_buffer; src->pub.skip_input_data = stdio_skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ src->pub.term_source = stdio_term_source; src->infile = f; src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ src->pub.next_input_byte = NULL; /* until buffer loaded */ jpeg_save_markers (&cinfo, EXIF_JPEG_MARKER, 0xffff); jpeg_read_header (&cinfo, TRUE); /* check for orientation tag */ is_otag = get_orientation (&cinfo); jpeg_start_decompress (&cinfo); cinfo.do_fancy_upsampling = FALSE; cinfo.do_block_smoothing = FALSE; pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, cinfo.out_color_components == 4 ? TRUE : FALSE, 8, cinfo.output_width, cinfo.output_height); if (!pixbuf) { jpeg_destroy_decompress (&cinfo); /* broken check for *error == NULL for robustness against * crappy JPEG library */ if (error && *error == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to load image, try exiting some applications to free memory")); } return NULL; } /* if orientation tag was found set an option to remember its value */ if (is_otag) { g_snprintf (otag_str, sizeof (otag_str), "%d", is_otag); gdk_pixbuf_set_option (pixbuf, "orientation", otag_str); } dptr = pixbuf->pixels; /* decompress all the lines, a few at a time */ while (cinfo.output_scanline < cinfo.output_height) { lptr = lines; for (i = 0; i < cinfo.rec_outbuf_height; i++) { *lptr++ = dptr; dptr += pixbuf->rowstride; } jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height); switch (cinfo.out_color_space) { case JCS_GRAYSCALE: explode_gray_into_buf (&cinfo, lines); break; case JCS_RGB: /* do nothing */ break; case JCS_CMYK: convert_cmyk_to_rgb (&cinfo, lines); break; default: g_object_unref (pixbuf); if (error && *error == NULL) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Unsupported JPEG color space (%s)"), colorspace_name (cinfo.out_color_space)); } jpeg_destroy_decompress (&cinfo); return NULL; } } jpeg_finish_decompress (&cinfo); jpeg_destroy_decompress (&cinfo); return pixbuf; }
static void SaveMetadataMarkers(j_decompress_ptr dinfo) { const unsigned int max_marker_length = 0xffff; jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length); // Exif/XMP jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length); // ICC profile }
int main(int argc, char **argv) { JSAMPARRAY buf = malloc(sizeof(JSAMPROW)*BUF_LINES); jpeg_saved_marker_ptr exif_marker, cmarker; MD5_CTX *MD5 = malloc(sizeof(MD5_CTX)); volatile int i; int c,j,lines_read, err_count; char ch; char namebuf[1024]; long fs; char *md5buf,digest[16],digest_text[33]; global_total_errors=0; if (rcsid); /* to keep compiler from not complaining about rcsid */ cinfo.err = jpeg_std_error(&jerr.pub); jpeg_create_decompress(&cinfo); jerr.pub.error_exit=my_error_exit; jerr.pub.output_message=my_output_message; if (!buf || !MD5) no_memory(); if (argc<2) { if (quiet_mode < 2) fprintf(stderr,"jpeginfo: file arguments missing\n" "Try 'jpeginfo " "--help" "' for more information.\n"); exit(1); } /* parse command line parameters */ while(1) { opt_index=0; if ( (c=getopt_long(argc,argv,"livVdcChqm:f:5", long_options,&opt_index)) == -1) break; switch (c) { case 'm': if (!strcasecmp(optarg,"all")) del_mode=0; else if (!strcasecmp(optarg,"erronly")) del_mode=1; else if (!quiet_mode) fprintf(stderr,"Unknown parameter for -m, --mode.\n"); break; case 'f': if (!strcmp(optarg,"-")) listfile=stdin; else if ((listfile=fopen(optarg,"r"))==NULL) { fprintf(stderr,"Cannot open file '%s'.\n",optarg); exit(2); } input_from_file=1; break; case 'v': verbose_mode=1; break; case 'V': fprintf(stderr,"jpeginfo v" VERSION " " HOST_TYPE "\nCopyright (c) Timo Kokkonen, 1995-2002.\n"); exit(0); case 'd': delete_mode=1; break; case 'c': check_mode=1; break; case 'h': p_usage(); break; case 'q': quiet_mode++; break; case 'l': list_mode=1; break; case 'i': longinfo_mode=1; break; case '5': md5_mode=1; break; case 'C': com_mode=1; break; case '?': break; default: if (!quiet_mode) fprintf(stderr,"jpeginfo: error parsing parameters.\n"); } } if (delete_mode && verbose_mode && !quiet_mode) fprintf(stderr,"jpeginfo: delete mode enabled (%s)\n", !del_mode?"normal":"errors only"); i=1; do { if (input_from_file) { if (!fgetstr(namebuf,sizeof(namebuf),listfile)) break; current=namebuf; } else current=argv[i]; if (current[0]==0) continue; if (current[0]=='-' && !input_from_file) continue; if (setjmp(jerr.setjmp_buffer)) { jpeg_abort_decompress(&cinfo); fclose(infile); if (list_mode && quiet_mode < 2) printf(" %s",current); if (quiet_mode < 2) printf(" [ERROR]\n"); if (delete_mode) delete_file(current,verbose_mode,quiet_mode); continue; } if ((infile=fopen(current,"r"))==NULL) { if (!quiet_mode) fprintf(stderr, "jpeginfo: can't open '%s'\n", current); continue; } if (is_dir(infile)) { fclose(infile); if (verbose_mode) printf("directory: %s skipped\n",current); continue; } fs=filesize(infile); if (md5_mode) { md5buf=malloc(fs); if (!md5buf) no_memory(); fread(md5buf,1,fs,infile); rewind(infile); MD5Init(MD5); MD5Update(MD5,md5buf,fs); MD5Final(digest,MD5); md2str(digest,digest_text); free(md5buf); } if (!list_mode && quiet_mode < 2) printf("%s ",current); global_error_counter=0; err_count=jerr.pub.num_warnings; if (com_mode) jpeg_save_markers(&cinfo, JPEG_COM, 0xffff); jpeg_save_markers(&cinfo, EXIF_JPEG_MARKER, 0xffff); jpeg_stdio_src(&cinfo, infile); jpeg_read_header(&cinfo, TRUE); /* check for Exif marker */ exif_marker=NULL; cmarker=cinfo.marker_list; while (cmarker) { if (cmarker->marker == EXIF_JPEG_MARKER) { if (!memcmp(cmarker->data,EXIF_IDENT_STRING,6)) exif_marker=cmarker; } cmarker=cmarker->next; } if (quiet_mode < 2) { printf("%4d x %-4d %2dbit ",(int)cinfo.image_width, (int)cinfo.image_height,(int)cinfo.num_components*8); if (exif_marker) printf("Exif "); else if (cinfo.saw_JFIF_marker) printf("JFIF "); else if (cinfo.saw_Adobe_marker) printf("Adobe "); else printf("n/a "); if (longinfo_mode) { printf("%s %s",(cinfo.progressive_mode?"Progressive":"Normal"), (cinfo.arith_code?"Arithmetic":"Huffman") ); if (cinfo.density_unit==1||cinfo.density_unit==2) printf(",%ddp%c",MIN(cinfo.X_density,cinfo.Y_density), (cinfo.density_unit==1?'i':'c') ); if (cinfo.CCIR601_sampling) printf(",CCIR601"); printf(" %7ld ",fs); } else printf("%c %7ld ",(cinfo.progressive_mode?'P':'N'),fs); if (md5_mode) printf("%s ",digest_text); if (list_mode) printf("%s ",current); if (com_mode) { cmarker=cinfo.marker_list; while (cmarker) { if (cmarker->marker == JPEG_COM) { printf("\""); for (j=0;j<cmarker->data_length;j++) { ch = cmarker->data[j]; if (ch < 32 || iscntrl(ch)) continue; printf("%c",cmarker->data[j]); } printf("\" "); } cmarker=cmarker->next; } } } if (check_mode) { cinfo.out_color_space=JCS_GRAYSCALE; /* to speed up the process... */ cinfo.scale_denom = 8; jpeg_start_decompress(&cinfo); for (j=0;j<BUF_LINES;j++) { buf[j]=malloc(sizeof(JSAMPLE)*cinfo.output_width* cinfo.out_color_components); if (!buf[j]) no_memory(); } while (cinfo.output_scanline < cinfo.output_height) { lines_read = jpeg_read_scanlines(&cinfo, buf,BUF_LINES); } jpeg_finish_decompress(&cinfo); for(j=0;j<BUF_LINES;j++) free(buf[j]); if (!global_error_counter) { if (quiet_mode < 2) printf(" [OK]\n"); } else { if (quiet_mode < 2) printf(" [WARNING]\n"); if (delete_mode && !del_mode) delete_file(current,verbose_mode,quiet_mode); } } else { /* !check_mode */ if (quiet_mode < 2) printf("\n"); jpeg_abort_decompress(&cinfo); } fclose(infile); } while (++i<argc || input_from_file); jpeg_destroy_decompress(&cinfo); free(buf); free(MD5); return (global_total_errors>0?1:0); /* return 1 if any errors found file(s) we checked */ }
static int load_jpeg_sub(RefImage *image, Value r, int info_only) { int channels; int inverted = FALSE; int rowbytes; uint8_t *data; struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; memset(&cinfo, 0, sizeof(cinfo)); memset(&jerr, 0, sizeof(jerr)); cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = fatal_jpeg_error; jerr.output_message = throw_jpeg_error; jpeg_create_decompress(&cinfo); jpeg_init_source(&cinfo, r); jpeg_save_markers(&cinfo, JPEG_APP0 + 14, 256); if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) { if (fg->error == VALUE_NULL) { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format"); } goto ERROR_END; } if (cinfo.image_width > MAX_IMAGE_SIZE || cinfo.image_height > MAX_IMAGE_SIZE) { fs->throw_errorf(mod_image, "ImageError", "Image size too large (max:%d)", MAX_IMAGE_SIZE); goto ERROR_END; } image->width = cinfo.image_width; image->height = cinfo.image_height; if (!jpeg_start_decompress(&cinfo)) { if (fg->error == VALUE_NULL) { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format (jpeg_start_decompress failed)"); } goto ERROR_END; } if (image->width != cinfo.output_width || image->height != cinfo.output_height) { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format"); goto ERROR_END; } switch (cinfo.jpeg_color_space) { case JCS_CMYK: case JCS_YCCK: cinfo.out_color_space = JCS_CMYK; break; case JCS_GRAYSCALE: cinfo.out_color_space = JCS_GRAYSCALE; break; default: cinfo.out_color_space = JCS_RGB; break; } if (cinfo.out_color_space == JCS_RGB) { if (cinfo.output_components != 3) { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format (output_components %d, expected 3)", cinfo.output_components); goto ERROR_END; } image->bands = BAND_RGB; channels = 3; } else if (cinfo.out_color_space == JCS_CMYK) { jpeg_saved_marker_ptr marker; if (cinfo.output_components != 4) { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format (output_components %d, expected 4)", cinfo.output_components); goto ERROR_END; } for (marker = cinfo.marker_list; marker != NULL; marker = marker->next) { if (marker->marker == (JPEG_APP0 + 14) && marker->data_length >= 12 && strncmp((const char*)marker->data, "Adobe", 5) == 0) { inverted = TRUE; break; } } image->bands = BAND_RGB; channels = 3; } else if (cinfo.out_color_space == JCS_GRAYSCALE) { if (cinfo.output_components != 1) { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format (output_components %d, expected 1)", cinfo.output_components); goto ERROR_END; } image->bands = BAND_L; channels = 1; } else { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format (Not suppoeter color_space)"); goto ERROR_END; } rowbytes = image->width * channels; image->pitch = rowbytes; if (!info_only) { if (rowbytes * image->height > fs->max_alloc) { fs->throw_error_select(THROW_MAX_ALLOC_OVER__INT, fs->max_alloc); goto ERROR_END; } data = malloc(rowbytes * image->height); if (cinfo.out_color_space == JCS_RGB || cinfo.out_color_space == JCS_GRAYSCALE) { JSAMPROW rows[1]; int i; for (i = 0; i < image->height; i++) { rows[0] = data + rowbytes * i; if (jpeg_read_scanlines(&cinfo, rows, 1) != 1) { if (fg->error == VALUE_NULL) { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format (jpeg_read_scanlines failed)"); } goto ERROR_END; } } } else if (cinfo.out_color_space == JCS_CMYK) { JSAMPROW rows[1]; int i; uint8_t *cmyk = malloc(image->width * 4); rows[0] = cmyk; for (i = 0; i < image->height; i++) { // CMYK CMYK ... if (jpeg_read_scanlines(&cinfo, rows, 1) != 1) { if (fg->error == VALUE_NULL) { fs->throw_errorf(mod_image, "ImageError", "Invalid JPEG format (jpeg_read_scanlines failed)"); } goto ERROR_END; } CMYK_to_RGB(data + rowbytes * i, cmyk, image->width, inverted); } free(cmyk); } image->data = data; } jpeg_destroy_decompress(&cinfo); return TRUE; ERROR_END: jpeg_destroy_decompress(&cinfo); return FALSE; }
int xps_decode_jpeg(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) { jpeg_decompress_data jddp; stream_DCT_state state; stream_cursor_read rp; stream_cursor_write wp; int code; int wlen; byte *wbuf; jpeg_saved_marker_ptr curr_marker; s_init_state((stream_state*)&state, &s_DCTD_template, ctx->memory); state.report_error = xps_report_error; s_DCTD_template.set_defaults((stream_state*)&state); state.jpeg_memory = ctx->memory; state.data.decompress = &jddp; jddp.templat = s_DCTD_template; jddp.memory = ctx->memory; jddp.scanline_buffer = NULL; if ((code = gs_jpeg_create_decompress(&state)) < 0) return gs_throw(-1, "cannot gs_jpeg_create_decompress"); s_DCTD_template.init((stream_state*)&state); rp.ptr = rbuf - 1; rp.limit = rbuf + rlen - 1; /* read the header only by not having a write buffer */ wp.ptr = 0; wp.limit = 0; /* Set up to save the ICC marker APP2. * According to the spec we should be getting APP1 APP2 and APP13. * Library gets APP0 and APP14. */ jpeg_save_markers(&(jddp.dinfo), 0xe2, 0xFFFF); code = s_DCTD_template.process((stream_state*)&state, &rp, &wp, true); if (code != 1) return gs_throw(-1, "premature EOF or error in jpeg"); /* Check if we had an ICC profile */ curr_marker = jddp.dinfo.marker_list; while (curr_marker != NULL) { if (curr_marker->marker == 0xe2) { /* Found ICC profile. Create a buffer and copy over now. * Strip JPEG APP2 14 byte header */ image->profilesize = curr_marker->data_length - 14; image->profile = xps_alloc(ctx, image->profilesize); if (image->profile) { /* If we can't create it, just ignore */ memcpy(image->profile, &(curr_marker->data[14]), image->profilesize); } break; } curr_marker = curr_marker->next; } image->width = jddp.dinfo.output_width; image->height = jddp.dinfo.output_height; image->comps = jddp.dinfo.output_components; image->bits = 8; image->stride = image->width * image->comps; if (image->comps == 1) image->colorspace = ctx->gray; if (image->comps == 3) image->colorspace = ctx->srgb; if (image->comps == 4) image->colorspace = ctx->cmyk; if (jddp.dinfo.density_unit == 1) { image->xres = jddp.dinfo.X_density; image->yres = jddp.dinfo.Y_density; } else if (jddp.dinfo.density_unit == 2) { image->xres = (int)(jddp.dinfo.X_density * 2.54 + 0.5); image->yres = (int)(jddp.dinfo.Y_density * 2.54 + 0.5); } else { image->xres = 96; image->yres = 96; } wlen = image->stride * image->height; wbuf = xps_alloc(ctx, wlen); if (!wbuf) return gs_throw1(gs_error_VMerror, "out of memory allocating samples: %d", wlen); image->samples = wbuf; wp.ptr = wbuf - 1; wp.limit = wbuf + wlen - 1; code = s_DCTD_template.process((stream_state*)&state, &rp, &wp, true); if (code != EOFC) return gs_throw1(-1, "error in jpeg (code = %d)", code); gs_jpeg_destroy(&state); return gs_okay; }
int main(int argc, char **argv) { struct jpeg_decompress_struct dinfo; struct jpeg_compress_struct cinfo; struct my_error_mgr jcerr,jderr; JSAMPARRAY buf = NULL; jvirt_barray_ptr *coef_arrays = NULL; char marker_str[256]; char tmpfilename[MAXPATHLEN],tmpdir[MAXPATHLEN]; char newname[MAXPATHLEN], dest_path[MAXPATHLEN]; volatile int i; int c,j, tmpfd, searchcount, searchdone; int opt_index = 0; long insize = 0, outsize = 0, lastsize = 0; int oldquality; double ratio; struct stat file_stat; jpeg_saved_marker_ptr cmarker; unsigned char *outbuffer = NULL; size_t outbuffersize; char *outfname = NULL; FILE *infile = NULL, *outfile = NULL; int marker_in_count, marker_in_size; int compress_err_count = 0; int decompress_err_count = 0; long average_count = 0; double average_rate = 0.0, total_save = 0.0; if (rcsid) ; /* so compiler won't complain about "unused" rcsid string */ umask(077); signal(SIGINT,own_signal_handler); signal(SIGTERM,own_signal_handler); /* initialize decompression object */ dinfo.err = jpeg_std_error(&jderr.pub); jpeg_create_decompress(&dinfo); jderr.pub.error_exit=my_error_exit; jderr.pub.output_message=my_output_message; jderr.jump_set = 0; /* initialize compression object */ cinfo.err = jpeg_std_error(&jcerr.pub); jpeg_create_compress(&cinfo); jcerr.pub.error_exit=my_error_exit; jcerr.pub.output_message=my_output_message; jcerr.jump_set = 0; if (argc<2) { if (!quiet_mode) fprintf(stderr,PROGRAMNAME ": file arguments missing\n" "Try '" PROGRAMNAME " --help' for more information.\n"); exit(1); } /* parse command line parameters */ while(1) { opt_index=0; if ((c=getopt_long(argc,argv,"d:hm:nstqvfVpPoT:S:b",long_options,&opt_index)) == -1) break; switch (c) { case 'm': { int tmpvar; if (sscanf(optarg,"%d",&tmpvar) == 1) { quality=tmpvar; if (quality < 0) quality=0; if (quality > 100) quality=100; } else fatal("invalid argument for -m, --max"); } break; case 'd': if (realpath(optarg,dest_path)==NULL || !is_directory(dest_path)) { fatal("invalid argument for option -d, --dest"); } strncat(dest_path,DIR_SEPARATOR_S,sizeof(dest_path)-strlen(dest_path)-1); if (verbose_mode) fprintf(stderr,"Destination directory: %s\n",dest_path); dest=1; break; case 'v': verbose_mode++; break; case 'h': print_usage(); exit(0); break; case 'q': quiet_mode=1; break; case 't': totals_mode=1; break; case 'n': noaction=1; break; case 'f': force=1; break; case 'b': csv=1; quiet_mode=1; break; case '?': break; case 'V': print_version(); exit(0); break; case 'o': overwrite_mode=1; break; case 'p': preserve_mode=1; break; case 'P': preserve_perms=1; break; case 's': save_exif=0; save_iptc=0; save_com=0; save_icc=0; save_xmp=0; break; case 'T': { int tmpvar; if (sscanf(optarg,"%d",&tmpvar) == 1) { threshold=tmpvar; if (threshold < 0) threshold=0; if (threshold > 100) threshold=100; } else fatal("invalid argument for -T, --threshold"); } break; case 'S': { unsigned int tmpvar; if (sscanf(optarg,"%u",&tmpvar) == 1) { if (tmpvar > 0 && tmpvar < 100 && optarg[strlen(optarg)-1] == '%' ) { target_size=-tmpvar; } else { target_size=tmpvar; } quality=100; } else fatal("invalid argument for -S, --size"); } break; } } /* check for '-' option indicating input is from stdin... */ i=1; while (argv[i]) { if (argv[i][0]=='-' && argv[i][1]==0) stdin_mode=1; i++; } if (stdin_mode) { stdout_mode=1; force=1; } if (stdout_mode) { logs_to_stdout=0; } if (all_normal && all_progressive) fatal("cannot specify both --all-normal and --all-progressive"); if (verbose_mode) { if (quality>=0 && target_size==0) fprintf(stderr,"Image quality limit set to: %d\n",quality); if (threshold>=0) fprintf(stderr,"Compression threshold (%%) set to: %d\n",threshold); if (all_normal) fprintf(stderr,"All output files will be non-progressive\n"); if (all_progressive) fprintf(stderr,"All output files will be progressive\n"); if (target_size > 0) fprintf(stderr,"Target size for output files set to: %u Kbytes.\n", target_size); if (target_size < 0) fprintf(stderr,"Target size for output files set to: %u%%\n", -target_size); } /* loop to process the input files */ i=1; do { if (stdin_mode) { infile=stdin; set_filemode_binary(infile); } else { if (!argv[i][0]) continue; if (argv[i][0]=='-') continue; if (strlen(argv[i]) >= MAXPATHLEN) { warn("skipping too long filename: %s",argv[i]); continue; } if (!noaction) { /* generate tmp dir & new filename */ if (dest) { STRNCPY(tmpdir,dest_path,sizeof(tmpdir)); STRNCPY(newname,dest_path,sizeof(newname)); if (!splitname(argv[i],tmpfilename,sizeof(tmpfilename))) fatal("splitname() failed for: %s",argv[i]); strncat(newname,tmpfilename,sizeof(newname)-strlen(newname)-1); } else { if (!splitdir(argv[i],tmpdir,sizeof(tmpdir))) fatal("splitdir() failed for: %s",argv[i]); STRNCPY(newname,argv[i],sizeof(newname)); } } retry_point: if (!is_file(argv[i],&file_stat)) { if (is_directory(argv[i])) warn("skipping directory: %s",argv[i]); else warn("skipping special file: %s",argv[i]); continue; } if ((infile=fopen(argv[i],"rb"))==NULL) { warn("cannot open file: %s", argv[i]); continue; } } if (setjmp(jderr.setjmp_buffer)) { /* error handler for decompress */ jpeg_abort_decompress(&dinfo); fclose(infile); if (buf) FREE_LINE_BUF(buf,dinfo.output_height); if (!quiet_mode || csv) fprintf(LOG_FH,csv ? ",,,,,error\n" : " [ERROR]\n"); decompress_err_count++; jderr.jump_set=0; continue; } else { jderr.jump_set=1; } if (!retry && (!quiet_mode || csv)) { fprintf(LOG_FH,csv ? "%s," : "%s ",(stdin_mode?"stdin":argv[i])); fflush(LOG_FH); } /* prepare to decompress */ global_error_counter=0; jpeg_save_markers(&dinfo, JPEG_COM, 0xffff); for (j=0;j<=15;j++) jpeg_save_markers(&dinfo, JPEG_APP0+j, 0xffff); jpeg_stdio_src(&dinfo, infile); jpeg_read_header(&dinfo, TRUE); /* check for Exif/IPTC/ICC/XMP markers */ marker_str[0]=0; marker_in_count=0; marker_in_size=0; cmarker=dinfo.marker_list; while (cmarker) { marker_in_count++; marker_in_size+=cmarker->data_length; if (cmarker->marker == EXIF_JPEG_MARKER && !memcmp(cmarker->data,EXIF_IDENT_STRING,EXIF_IDENT_STRING_SIZE)) strncat(marker_str,"Exif ",sizeof(marker_str)-strlen(marker_str)-1); if (cmarker->marker == IPTC_JPEG_MARKER) strncat(marker_str,"IPTC ",sizeof(marker_str)-strlen(marker_str)-1); if (cmarker->marker == ICC_JPEG_MARKER && !memcmp(cmarker->data,ICC_IDENT_STRING,ICC_IDENT_STRING_SIZE)) strncat(marker_str,"ICC ",sizeof(marker_str)-strlen(marker_str)-1); if (cmarker->marker == XMP_JPEG_MARKER && !memcmp(cmarker->data,XMP_IDENT_STRING,XMP_IDENT_STRING_SIZE)) strncat(marker_str,"XMP ",sizeof(marker_str)-strlen(marker_str)-1); cmarker=cmarker->next; } if (verbose_mode > 1) fprintf(LOG_FH,"%d markers found in input file (total size %d bytes)\n", marker_in_count,marker_in_size); if (!retry && (!quiet_mode || csv)) { fprintf(LOG_FH,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width, (int)dinfo.image_height,(int)dinfo.num_components*8, (dinfo.progressive_mode?'P':'N')); if (!csv) { fprintf(LOG_FH,"%s",marker_str); if (dinfo.saw_Adobe_marker) fprintf(LOG_FH,"Adobe "); if (dinfo.saw_JFIF_marker) fprintf(LOG_FH,"JFIF "); } fflush(LOG_FH); } if ((insize=file_size(infile)) < 0) fatal("failed to stat() input file"); /* decompress the file */ if (quality>=0 && !retry) { jpeg_start_decompress(&dinfo); /* allocate line buffer to store the decompressed image */ buf = malloc(sizeof(JSAMPROW)*dinfo.output_height); if (!buf) fatal("not enough memory"); for (j=0;j<dinfo.output_height;j++) { buf[j]=malloc(sizeof(JSAMPLE)*dinfo.output_width* dinfo.out_color_components); if (!buf[j]) fatal("not enough memory"); } while (dinfo.output_scanline < dinfo.output_height) { jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline], dinfo.output_height-dinfo.output_scanline); } } else { coef_arrays = jpeg_read_coefficients(&dinfo); } if (!retry && !quiet_mode) { if (global_error_counter==0) fprintf(LOG_FH," [OK] "); else fprintf(LOG_FH," [WARNING] "); fflush(LOG_FH); } if (dest && !noaction) { if (file_exists(newname) && !overwrite_mode) { warn("target file already exists: %s\n",newname); jpeg_abort_decompress(&dinfo); fclose(infile); if (buf) FREE_LINE_BUF(buf,dinfo.output_height); continue; } } if (setjmp(jcerr.setjmp_buffer)) { /* error handler for compress failures */ jpeg_abort_compress(&cinfo); jpeg_abort_decompress(&dinfo); fclose(infile); if (!quiet_mode) fprintf(LOG_FH," [Compress ERROR]\n"); if (buf) FREE_LINE_BUF(buf,dinfo.output_height); compress_err_count++; jcerr.jump_set=0; continue; } else { jcerr.jump_set=1; } lastsize = 0; searchcount = 0; searchdone = 0; oldquality = 200; binary_search_loop: /* allocate memory buffer that should be large enough to store the output JPEG... */ if (outbuffer) free(outbuffer); outbuffersize=insize + 32768; outbuffer=malloc(outbuffersize); if (!outbuffer) fatal("not enough memory"); /* setup custom "destination manager" for libjpeg to write to our buffer */ jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536); if (quality>=0 && !retry) { /* lossy "optimization" ... */ cinfo.in_color_space=dinfo.out_color_space; cinfo.input_components=dinfo.output_components; cinfo.image_width=dinfo.image_width; cinfo.image_height=dinfo.image_height; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo,quality,TRUE); if ( (dinfo.progressive_mode || all_progressive) && !all_normal ) jpeg_simple_progression(&cinfo); cinfo.optimize_coding = TRUE; j=0; jpeg_start_compress(&cinfo,TRUE); /* write markers */ write_markers(&dinfo,&cinfo); /* write image */ while (cinfo.next_scanline < cinfo.image_height) { jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], dinfo.output_height); } } else { /* lossless "optimization" ... */ jpeg_copy_critical_parameters(&dinfo, &cinfo); if ( (dinfo.progressive_mode || all_progressive) && !all_normal ) jpeg_simple_progression(&cinfo); cinfo.optimize_coding = TRUE; /* write image */ jpeg_write_coefficients(&cinfo, coef_arrays); /* write markers */ write_markers(&dinfo,&cinfo); } jpeg_finish_compress(&cinfo); outsize=outbuffersize; if (target_size != 0 && !retry) { /* perform (binary) search to try to reach target file size... */ long osize = outsize/1024; long isize = insize/1024; long tsize = target_size; if (tsize < 0) { tsize=((-target_size)*insize/100)/1024; if (tsize < 1) tsize=1; } if (osize == tsize || searchdone || searchcount >= 8 || tsize > isize) { if (searchdone < 42 && lastsize > 0) { if (abs(osize-tsize) > abs(lastsize-tsize)) { if (verbose_mode) fprintf(LOG_FH,"(revert to %d)",oldquality); searchdone=42; quality=oldquality; goto binary_search_loop; } } if (verbose_mode) fprintf(LOG_FH," "); } else { int newquality; int dif = floor((abs(oldquality-quality)/2.0)+0.5); if (osize > tsize) { newquality=quality-dif; if (dif < 1) { newquality--; searchdone=1; } if (newquality < 0) { newquality=0; searchdone=2; } } else { newquality=quality+dif; if (dif < 1) { newquality++; searchdone=3; } if (newquality > 100) { newquality=100; searchdone=4; } } oldquality=quality; quality=newquality; if (verbose_mode) fprintf(LOG_FH,"(try %d)",quality); lastsize=osize; searchcount++; goto binary_search_loop; } } if (buf) FREE_LINE_BUF(buf,dinfo.output_height); jpeg_finish_decompress(&dinfo); fclose(infile); if (quality>=0 && outsize>=insize && !retry && !stdin_mode) { if (verbose_mode) fprintf(LOG_FH,"(retry w/lossless) "); retry=1; goto retry_point; } retry=0; ratio=(insize-outsize)*100.0/insize; if (!quiet_mode || csv) fprintf(LOG_FH,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio); average_count++; average_rate+=(ratio<0 ? 0.0 : ratio); if ((outsize < insize && ratio >= threshold) || force) { total_save+=(insize-outsize)/1024.0; if (!quiet_mode || csv) fprintf(LOG_FH,csv ? "optimized\n" : "optimized.\n"); if (noaction) continue; if (stdout_mode) { outfname=NULL; set_filemode_binary(stdout); if (fwrite(outbuffer,outbuffersize,1,stdout) != 1) fatal("write failed to stdout"); } else { if (preserve_perms && !dest) { /* make backup of the original file */ snprintf(tmpfilename,sizeof(tmpfilename),"%s.jpegoptim.bak",newname); if (verbose_mode > 1 && !quiet_mode) fprintf(LOG_FH,"creating backup of original image as: %s\n",tmpfilename); if (file_exists(tmpfilename)) fatal("backup file already exists: %s",tmpfilename); if (copy_file(newname,tmpfilename)) fatal("failed to create backup of original file"); if ((outfile=fopen(newname,"wb"))==NULL) fatal("error opening output file: %s", newname); outfname=newname; } else { #ifdef HAVE_MKSTEMPS /* rely on mkstemps() to create us temporary file safely... */ snprintf(tmpfilename,sizeof(tmpfilename), "%sjpegoptim-%d-%d.XXXXXX.tmp", tmpdir, (int)getuid(), (int)getpid()); if ((tmpfd = mkstemps(tmpfilename,4)) < 0) fatal("error creating temp file: mkstemps() failed"); if ((outfile=fdopen(tmpfd,"wb"))==NULL) #else /* if platform is missing mkstemps(), try to create at least somewhat "safe" temp file... */ snprintf(tmpfilename,sizeof(tmpfilename), "%sjpegoptim-%d-%d.%d.tmp", tmpdir, (int)getuid(), (int)getpid(),time(NULL)); tmpfd=0; if ((outfile=fopen(tmpfilename,"wb"))==NULL) #endif fatal("error opening temporary file: %s",tmpfilename); outfname=tmpfilename; } if (verbose_mode > 1 && !quiet_mode) fprintf(LOG_FH,"writing %lu bytes to file: %s\n", (long unsigned int)outbuffersize, outfname); if (fwrite(outbuffer,outbuffersize,1,outfile) != 1) fatal("write failed to file: %s", outfname); fclose(outfile); } if (outfname) { if (preserve_mode) { /* preserve file modification time */ struct utimbuf time_save; time_save.actime=file_stat.st_atime; time_save.modtime=file_stat.st_mtime; if (utime(outfname,&time_save) != 0) warn("failed to reset output file time/date"); } if (preserve_perms && !dest) { /* original file was already replaced, remove backup... */ if (delete_file(tmpfilename)) warn("failed to remove backup file: %s",tmpfilename); } else { /* make temp file to be the original file... */ /* preserve file mode */ if (chmod(outfname,(file_stat.st_mode & 0777)) != 0) warn("failed to set output file mode"); /* preserve file group (and owner if run by root) */ if (chown(outfname, (geteuid()==0 ? file_stat.st_uid : -1), file_stat.st_gid) != 0) warn("failed to reset output file group/owner"); if (verbose_mode > 1 && !quiet_mode) fprintf(LOG_FH,"renaming: %s to %s\n",outfname,newname); if (rename_file(outfname,newname)) fatal("cannot rename temp file"); } } } else { if (!quiet_mode || csv) fprintf(LOG_FH,csv ? "skipped\n" : "skipped.\n"); } } while (++i<argc && !stdin_mode); if (totals_mode && !quiet_mode) fprintf(LOG_FH,"Average ""compression"" (%ld files): %0.2f%% (%0.0fk)\n", average_count, average_rate/average_count, total_save); jpeg_destroy_decompress(&dinfo); jpeg_destroy_compress(&cinfo); return (decompress_err_count > 0 || compress_err_count > 0 ? 1 : 0);; }