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); } } }
int main(int argc, char **argv) { char tmpfilename[MAXPATHLEN],tmpdir[MAXPATHLEN]; char newname[MAXPATHLEN], dest_path[MAXPATHLEN]; volatile int i; int c,j, err_count; int opt_index = 0; long insize,outsize; double ratio; struct utimbuf time_save; jpeg_saved_marker_ptr cmarker; if (rcsid); /* so compiler won't complain about "unused" rcsid string */ 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; /* 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; if (argc<2) { if (!quiet_mode) fprintf(stderr,"jpegoptim: file arguments missing\n" "Try 'jpegoptim --help' for more information.\n"); exit(1); } /* parse command line parameters */ while(1) { opt_index=0; if ((c=getopt_long(argc,argv,"d:hm:ntqvfVpoT:",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"); } if (verbose_mode) fprintf(stderr,"Destination directory: %s\n",dest_path); dest=1; break; case 'v': verbose_mode++; break; case 'h': p_usage(); break; case 'q': quiet_mode=1; break; case 't': totals_mode=1; break; case 'n': noaction=1; break; case 'f': force=1; break; case '?': break; case 'V': printf("jpegoptim v%s %s\n",VERSIO,HOST_TYPE); printf("Copyright (c) Timo Kokkonen, 1996-2011.\n"); exit(0); break; case 'o': overwrite_mode=1; break; case 'p': preserve_mode=1; break; case 's': save_exif=0; save_iptc=0; save_com=0; save_icc=0; break; case 'c': save_com=0; break; case 'e': save_exif=0; break; case 'i': save_iptc=0; break; case 'P': save_icc=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; default: if (!quiet_mode) fprintf(stderr,"jpegoptim: error parsing parameters.\n"); } } if (verbose_mode && (quality>=0)) fprintf(stderr,"Image quality limit set to: %d\n",quality); if (verbose_mode && (threshold>=0)) fprintf(stderr,"Compression treshold (%%) set to: %d\n",threshold); /* loop to process the input files */ i=1; do { if (!argv[i][0]) continue; if (argv[i][0]=='-') continue; if (!noaction) { /* generate temp (& new) filename */ if (dest) { strncpy(tmpdir,dest_path,sizeof(tmpdir)); strncpy(newname,dest_path,sizeof(newname)); if (tmpdir[strlen(tmpdir)-1] != '/') { strncat(tmpdir,"/",sizeof(tmpdir)-strlen(tmpdir)); strncat(newname,"/",sizeof(newname)-strlen(newname)); } strncat(newname,(char*)basename(argv[i]), sizeof(newname)-strlen(newname)); } else { if (!splitdir(argv[i],tmpdir,sizeof(tmpdir))) fatal("splitdir() failed!"); strncpy(newname,argv[i],sizeof(newname)); } snprintf(tmpfilename,sizeof(tmpfilename), "%sjpegoptim-%d-%d.tmp", tmpdir, (int)getuid(), (int)getpid()); } retry_point: if ((infile=fopen(argv[i],"r"))==NULL) { if (!quiet_mode) fprintf(stderr, "jpegoptim: can't open %s\n", argv[i]); continue; } if (is_dir(infile,&time_save.actime,&time_save.modtime)) { fclose(infile); if (verbose_mode) printf("directory: %s skipped\n",argv[i]); continue; } /* setup error handling for decompress */ if (setjmp(jderr.setjmp_buffer)) { jpeg_abort_decompress(&dinfo); fclose(infile); if (buf) { for (j=0;j<dinfo.output_height;j++) free(buf[j]); free(buf); buf=NULL; } printf(" [ERROR]\n"); continue; } if (!retry) { printf("%s ",argv[i]); fflush(stdout); } /* prepare to decompress */ global_error_counter=0; err_count=jderr.pub.num_warnings; if (save_com) jpeg_save_markers(&dinfo, JPEG_COM, 0xffff); if (save_iptc) jpeg_save_markers(&dinfo, IPTC_JPEG_MARKER, 0xffff); if (save_exif) jpeg_save_markers(&dinfo, EXIF_JPEG_MARKER, 0xffff); if (save_icc) jpeg_save_markers(&dinfo, ICC_JPEG_MARKER, 0xffff); jpeg_stdio_src(&dinfo, infile); jpeg_read_header(&dinfo, TRUE); /* check for Exif/IPTC markers */ exif_marker=NULL; iptc_marker=NULL; icc_marker=NULL; cmarker=dinfo.marker_list; while (cmarker) { if (cmarker->marker == EXIF_JPEG_MARKER) { if (!memcmp(cmarker->data,EXIF_IDENT_STRING,EXIF_IDENT_STRING_SIZE)) exif_marker=cmarker; } if (cmarker->marker == IPTC_JPEG_MARKER) { iptc_marker=cmarker; } if (cmarker->marker == ICC_JPEG_MARKER) { if (!memcmp(cmarker->data,ICC_IDENT_STRING,ICC_IDENT_STRING_SIZE)) icc_marker=cmarker; } cmarker=cmarker->next; } if (!retry) { printf("%dx%d %dbit ",(int)dinfo.image_width, (int)dinfo.image_height,(int)dinfo.num_components*8); if (exif_marker) printf("Exif "); if (iptc_marker) printf("IPTC "); if (icc_marker) printf("ICC "); if (dinfo.saw_Adobe_marker) printf("Adobe "); if (dinfo.saw_JFIF_marker) printf("JFIF "); fflush(stdout); } insize=file_size(infile); /* decompress the file */ if (quality>=0 && !retry) { jpeg_start_decompress(&dinfo); 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) { if (!global_error_counter) printf(" [OK] "); else printf(" [WARNING] "); fflush(stdout); } if (dest && !noaction) { if (file_exists(newname) && !overwrite_mode) { fprintf(stderr,"target file already exists!\n"); jpeg_abort_decompress(&dinfo); fclose(infile); if (buf) { for (j=0;j<dinfo.output_height;j++) free(buf[j]); free(buf); buf=NULL; } continue; } } if (noaction) { outfname=NULL; if ((outfile=tmpfile())==NULL) fatal("error opening temp file"); } else { outfname=tmpfilename; if ((outfile=fopen(outfname,"w"))==NULL) fatal("error opening target file"); } if (setjmp(jcerr.setjmp_buffer)) { jpeg_abort_compress(&cinfo); jpeg_abort_decompress(&dinfo); fclose(outfile); if (infile) fclose(infile); printf(" [Compress ERROR]\n"); if (buf) { for (j=0;j<dinfo.output_height;j++) free(buf[j]); free(buf); buf=NULL; } if (file_exists(outfname)) delete_file(outfname); outfname=NULL; continue; } jpeg_stdio_dest(&cinfo, outfile); 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); cinfo.optimize_coding = TRUE; j=0; jpeg_start_compress(&cinfo,TRUE); /* write markers */ write_markers(&dinfo,&cinfo); while (cinfo.next_scanline < cinfo.image_height) { jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], dinfo.output_height); } for (j=0;j<dinfo.output_height;j++) free(buf[j]); free(buf); buf=NULL; } else { /* lossless "optimization" ... */ jpeg_copy_critical_parameters(&dinfo, &cinfo); cinfo.optimize_coding = TRUE; jpeg_write_coefficients(&cinfo, coef_arrays); /* write markers */ write_markers(&dinfo,&cinfo); } jpeg_finish_compress(&cinfo); jpeg_finish_decompress(&dinfo); fclose(infile); outsize=file_size(outfile); fclose(outfile); if (preserve_mode && !noaction) { if (utime(outfname,&time_save) != 0) { fprintf(stderr,"jpegoptim: failed to reset output file time/date\n"); } } if (quality>=0 && outsize>=insize && !retry) { if (verbose_mode) printf("(retry w/lossless) "); retry=1; goto retry_point; } retry=0; ratio=(insize-outsize)*100.0/insize; printf("%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; printf("optimized.\n"); if (noaction) continue; if (verbose_mode > 1 && !quiet_mode) fprintf(stderr,"renaming: %s to %s\n",outfname,newname); if (rename(outfname,newname)) fatal("cannot rename temp file"); } else { printf("skipped.\n"); if (!noaction) delete_file(outfname); } } while (++i<argc); if (totals_mode&&!quiet_mode) printf("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 0; }
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);; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { if ((dib) && (handle)) { try { // Check dib format const char *sError = "only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG"; FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); WORD bpp = (WORD)FreeImage_GetBPP(dib); if ((bpp != 24) && (bpp != 8)) throw sError; if(bpp == 8) { // allow grey, reverse grey and palette if ((color_type != FIC_MINISBLACK) && (color_type != FIC_MINISWHITE) && (color_type != FIC_PALETTE)) throw sError; } struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; // Step 1: allocate and initialize JPEG compression object cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpeg_error_exit; jerr.output_message = jpeg_output_message; // Now we can initialize the JPEG compression object jpeg_create_compress(&cinfo); // Step 2: specify data destination (eg, a file) jpeg_freeimage_dst(&cinfo, handle, io); // Step 3: set parameters for compression cinfo.image_width = FreeImage_GetWidth(dib); cinfo.image_height = FreeImage_GetHeight(dib); switch(color_type) { case FIC_MINISBLACK : case FIC_MINISWHITE : cinfo.in_color_space = JCS_GRAYSCALE; cinfo.input_components = 1; break; default : cinfo.in_color_space = JCS_RGB; cinfo.input_components = 3; break; } jpeg_set_defaults(&cinfo); // progressive-JPEG support if((flags & JPEG_PROGRESSIVE) == JPEG_PROGRESSIVE) { jpeg_simple_progression(&cinfo); } // Set JFIF density parameters from the DIB data cinfo.X_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib)); cinfo.Y_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib)); cinfo.density_unit = 1; // dots / inch // set subsampling options if required if(cinfo.in_color_space == JCS_RGB) { if((flags & JPEG_SUBSAMPLING_411) == JPEG_SUBSAMPLING_411) { // 4:1:1 (4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100% // the horizontal color resolution is quartered cinfo.comp_info[0].h_samp_factor = 4; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_420) == JPEG_SUBSAMPLING_420) { // 4:2:0 (2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50% // the chrominance resolution in both the horizontal and vertical directions is cut in half cinfo.comp_info[0].h_samp_factor = 2; // Y cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_422) == JPEG_SUBSAMPLING_422){ //2x1 (low) // 4:2:2 (2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100% // half of the horizontal resolution in the chrominance is dropped (Cb & Cr), // while the full resolution is retained in the vertical direction, with respect to the luminance cinfo.comp_info[0].h_samp_factor = 2; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_444) == JPEG_SUBSAMPLING_444){ //1x1 (no subsampling) // 4:4:4 (1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100% // the resolution of chrominance information (Cb & Cr) is preserved // at the same rate as the luminance (Y) information cinfo.comp_info[0].h_samp_factor = 1; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } } // Step 4: set quality // the first 7 bits are reserved for low level quality settings // the other bits are high level (i.e. enum-ish) int quality; if ((flags & JPEG_QUALITYBAD) == JPEG_QUALITYBAD) { quality = 10; } else if ((flags & JPEG_QUALITYAVERAGE) == JPEG_QUALITYAVERAGE) { quality = 25; } else if ((flags & JPEG_QUALITYNORMAL) == JPEG_QUALITYNORMAL) { quality = 50; } else if ((flags & JPEG_QUALITYGOOD) == JPEG_QUALITYGOOD) { quality = 75; } else if ((flags & JPEG_QUALITYSUPERB) == JPEG_QUALITYSUPERB) { quality = 100; } else { if ((flags & 0x7F) == 0) { quality = 75; } else { quality = flags & 0x7F; } } jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */ // Step 5: Start compressor jpeg_start_compress(&cinfo, TRUE); // Step 6: Write special markers write_markers(&cinfo, dib); // Step 7: while (scan lines remain to be written) if(color_type == FIC_RGB) { // 24-bit RGB image : need to swap red and blue channels unsigned pitch = FreeImage_GetPitch(dib); BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE)); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } while (cinfo.next_scanline < cinfo.image_height) { // get a copy of the scanline memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // swap R and B channels BYTE *target_p = target; for(unsigned x = 0; x < cinfo.image_width; x++) { INPLACESWAP(target_p[0], target_p[2]); target_p += 3; } #endif // write the scanline jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } else if(color_type == FIC_MINISBLACK) { // 8-bit standard greyscale images while (cinfo.next_scanline < cinfo.image_height) { JSAMPROW b = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); jpeg_write_scanlines(&cinfo, &b, 1); } } else if(color_type == FIC_PALETTE) { // 8-bit palettized images are converted to 24-bit images RGBQUAD *palette = FreeImage_GetPalette(dib); BYTE *target = (BYTE*)malloc(cinfo.image_width * 3); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } while (cinfo.next_scanline < cinfo.image_height) { BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); FreeImage_ConvertLine8To24(target, source, cinfo.image_width, palette); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // swap R and B channels BYTE *target_p = target; for(unsigned x = 0; x < cinfo.image_width; x++) { INPLACESWAP(target_p[0], target_p[2]); target_p += 3; } #endif jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } else if(color_type == FIC_MINISWHITE) { // reverse 8-bit greyscale image, so reverse grey value on the fly unsigned i; BYTE reverse[256]; BYTE *target = (BYTE *)malloc(cinfo.image_width); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } for(i = 0; i < 256; i++) { reverse[i] = (BYTE)(255 - i); } while(cinfo.next_scanline < cinfo.image_height) { BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); for(i = 0; i < cinfo.image_width; i++) { target[i] = reverse[ source[i] ]; } jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } // Step 8: Finish compression jpeg_finish_compress(&cinfo); // Step 9: release JPEG compression object jpeg_destroy_compress(&cinfo); return TRUE; } catch (const char *text) { FreeImage_OutputMessageProc(s_format_id, text); return FALSE; } catch (FREE_IMAGE_FORMAT) { return FALSE; } } return FALSE; }