int mjpeg_encoder_encode_frame(MJpegEncoder *encoder, uint8_t *buffer, size_t buffer_len) { struct jpeg_destination_mgr destmgr; uint8_t *frame; int n; destmgr.next_output_byte = buffer; destmgr.free_in_buffer = buffer_len; destmgr.init_destination = init_destination; destmgr.empty_output_buffer = empty_output_buffer; destmgr.term_destination = term_destination; encoder->cinfo.dest = &destmgr; encoder->cinfo.image_width = encoder->width; encoder->cinfo.image_height = encoder->height; encoder->cinfo.input_components = 3; encoder->cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&encoder->cinfo); encoder->cinfo.dct_method = JDCT_IFAST; jpeg_set_quality(&encoder->cinfo, encoder->quality, TRUE); jpeg_start_compress(&encoder->cinfo, encoder->first_frame); frame = encoder->frame; while (encoder->cinfo.next_scanline < encoder->cinfo.image_height) { n = jpeg_write_scanlines(&encoder->cinfo, &frame, 1); if (n == 0) { /* Not enough space */ jpeg_abort_compress(&encoder->cinfo); return 0; } frame += encoder->stride; } jpeg_finish_compress(&encoder->cinfo); encoder->first_frame = FALSE; return destmgr.next_output_byte - buffer; }
static void ufo_jpeg_writer_write (UfoWriter *writer, gpointer data, UfoRequisition *requisition, UfoBufferDepth depth) { UfoJpegWriterPrivate *priv; gint row_stride; priv = UFO_JPEG_WRITER_GET_PRIVATE (writer); priv->cinfo.image_width = requisition->dims[0]; priv->cinfo.image_height = requisition->dims[1]; priv->cinfo.input_components = 1; priv->cinfo.in_color_space = JCS_GRAYSCALE; jpeg_stdio_dest (&priv->cinfo, priv->fp); jpeg_set_defaults (&priv->cinfo); jpeg_set_quality (&priv->cinfo, priv->quality, 1); jpeg_start_compress (&priv->cinfo, TRUE); /* We have to ignore the given bit depth for JPEG. Note that this way, we * might convert data twice because the parent ufo_writer_write already * converts to the given bit depth. */ ufo_writer_convert_inplace (data, requisition, UFO_BUFFER_DEPTH_8U); row_stride = (gint) requisition->dims[0]; while (priv->cinfo.next_scanline < priv->cinfo.image_height) { JSAMPROW row_pointer[1]; row_pointer[0] = (JSAMPROW) (((gchar *) data) + priv->cinfo.next_scanline * row_stride); jpeg_write_scanlines (&priv->cinfo, row_pointer, 1); } jpeg_finish_compress (&priv->cinfo); jpeg_abort_compress (&priv->cinfo); }
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); } } }
bool TCJpegImageFormat::saveFile(TCImage *image, FILE *file) { bool retValue = false; bool canceled = false; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; if (image->getDataFormat() != TCRgb8 && image->getDataFormat() != TCRgba8) { return false; } if (setup(cinfo, jerr)) { std::vector<TCByte> row; TCByte *imageData; int rowSize; int offset = 0; int dir = 1; TCByte *rowBytes = NULL; TCJpegOptions *options = (TCJpegOptions *)image->getCompressionOptions(); imageWidth = image->getWidth(); imageHeight = image->getHeight(); this->image = image; // WARNING: Do NOT put any C++ objects that need destructors inside the // following if statement. Doing so will result in a memory leak if any // error occurs. #ifdef WIN32 #pragma warning( push ) #pragma warning( disable : 4611 ) #endif // WIN32 if (setjmp(jumpBuf)) #ifdef WIN32 #pragma warning( pop ) #endif // WIN32 { // If we get here, there was an error below. // Note: jpeg_destroy_compress is called automatically by the JPEG // library error handling code. We really are good to just exit. return false; } jpeg_stdio_dest(&cinfo, file); callProgressCallback(_UC("SavingJPG"), 0.0f); cinfo.in_color_space = JCS_RGB; cinfo.image_width = imageWidth; cinfo.image_height = image->getHeight(); cinfo.input_components = 3; imageData = image->getImageData(); rowSize = image->getRowSize(); if (image->getFlipped()) { offset = rowSize * (imageHeight - 1); dir = -1; } if (image->getDataFormat() == TCRgba8) { row.resize(image->getWidth() * 3); rowBytes = &row[0]; } jpeg_set_defaults(&cinfo); jpeg_default_colorspace(&cinfo); jpeg_set_quality(&cinfo, options->getQuality(), FALSE); switch (options->getSubSampling()) { case TCJpegOptions::SS420: // 4:2:0 cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; break; case TCJpegOptions::SS422: // 4:2:2 cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; break; case TCJpegOptions::SS444: // 4:4:4 cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; break; } if (options->getProgressive()) { jpeg_simple_progression(&cinfo); } jpeg_start_compress(&cinfo, TRUE); while (cinfo.next_scanline < cinfo.image_height && !canceled) { unsigned int result; if (image->getDataFormat() == TCRgb8) { rowBytes = &imageData[offset]; } else { int dstIndex = 0; int srcIndex = offset; // Convert RGBA to RGB. for (unsigned int x = 0; x < imageWidth; x++) { rowBytes[dstIndex++] = imageData[srcIndex++]; rowBytes[dstIndex++] = imageData[srcIndex++]; rowBytes[dstIndex++] = imageData[srcIndex++]; srcIndex++; } } result = jpeg_write_scanlines(&cinfo, &rowBytes, 1); offset += result * rowSize * dir; if (!callProgressCallback(NULL, (float)cinfo.next_scanline / (float)cinfo.image_height)) { canceled = true; } } if (canceled) { jpeg_abort_compress(&cinfo); } else { jpeg_finish_compress(&cinfo); retValue = true; } jpeg_destroy_compress(&cinfo); } callProgressCallback(NULL, 2.0f); return retValue && !canceled; }
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);; }
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; }
static gboolean background_jpeg_save (PreviewPersistent *pp) { gint yend; if (pp->abort_me || (pp->cinfo.next_scanline >= pp->cinfo.image_height)) { /* clean up... */ if (pp->abort_me) { jpeg_abort_compress (&(pp->cinfo)); } else { jpeg_finish_compress (&(pp->cinfo)); } fclose (pp->outfile); jpeg_destroy_compress (&(pp->cinfo)); g_free (pp->data); if (pp->buffer) g_object_unref (pp->buffer); /* display the preview stuff */ if (!pp->abort_me) { GFile *file = g_file_new_for_path (pp->file_name); GFileInfo *info; gchar *text; GError *error = NULL; info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, &error); if (info) { goffset size = g_file_info_get_size (info); gchar *size_text; size_text = g_format_size (size); text = g_strdup_printf (_("File size: %s"), size_text); g_free (size_text); g_object_unref (info); } else { text = g_strdup_printf (_("File size: %s"), error->message); g_clear_error (&error); } gtk_label_set_text (GTK_LABEL (preview_size), text); g_free (text); g_object_unref (file); /* and load the preview */ load_image (pp->file_name, GIMP_RUN_NONINTERACTIVE, TRUE, NULL, NULL); } /* we cleanup here (load_image doesn't run in the background) */ g_unlink (pp->file_name); g_free (pp); prev_p = NULL; gimp_displays_flush (); gdk_flush (); return FALSE; } else { if ((pp->cinfo.next_scanline % pp->tile_height) == 0) { yend = pp->cinfo.next_scanline + pp->tile_height; yend = MIN (yend, pp->cinfo.image_height); gegl_buffer_get (pp->buffer, GEGL_RECTANGLE (0, pp->cinfo.next_scanline, pp->cinfo.image_width, (yend - pp->cinfo.next_scanline)), 1.0, pp->format, pp->data, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); pp->src = pp->data; } jpeg_write_scanlines (&(pp->cinfo), (JSAMPARRAY) &(pp->src), 1); pp->src += pp->rowstride; return TRUE; } }
// Take a buffer containing a jpeg and return an optimized jpeg int optimizeJPEG(unsigned char *inputbuffer, unsigned long inputsize, unsigned char **outputbuffer, unsigned long *outputsize, int quality) { jvirt_barray_ptr *coef_arrays = NULL; JSAMPARRAY buf = NULL; struct jpeg_decompress_struct dinfo; struct jpeg_compress_struct cinfo; int j; int all_normal = 1; int all_progressive = 0; if (quality > 100) quality = 100; /* 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; /* setup error handling for decompress */ if (setjmp(jderr.setjmp_buffer)) { jpeg_abort_decompress(&dinfo); jpeg_destroy_decompress(&dinfo); jpeg_abort_compress(&cinfo); jpeg_destroy_compress(&cinfo); if (buf) { for (j=0;j<dinfo.output_height;j++) free(buf[j]); free(buf); buf=NULL; } outputsize = 0; outputbuffer = NULL; return 2; } /* prepare to decompress */ jpeg_mem_src(&dinfo, inputbuffer, inputsize); if (jpeg_read_header(&dinfo, TRUE) != JPEG_HEADER_OK) { return 2; } jpeg_mem_dest(&cinfo, outputbuffer, outputsize); printf("Proc: Image is %d by %d with %d components target quality:%d\n", dinfo.output_width, dinfo.output_height, dinfo.output_components, quality); if (quality>-1 ) { jpeg_start_decompress(&dinfo); buf = malloc(sizeof(JSAMPROW)*dinfo.output_height); if (!buf) { return 2; } for (j=0;j<dinfo.output_height;j++) { buf[j]=malloc(sizeof(JSAMPLE)*dinfo.output_width* dinfo.out_color_components); if (!buf[j]) return 2; } 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 (setjmp(jcerr.setjmp_buffer)) { jpeg_abort_compress(&cinfo); jpeg_abort_decompress(&dinfo); printf(" [Compress ERROR]\n"); if (buf) { for (j=0;j<dinfo.output_height;j++) free(buf[j]); free(buf); buf=NULL; } outputsize = 0; return 2; } if (quality>-1) { 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 image */ while (cinfo.next_scanline < cinfo.image_height) { jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], dinfo.output_height); } } else { jpeg_copy_critical_parameters(&dinfo, &cinfo); cinfo.optimize_coding = TRUE; jpeg_write_coefficients(&cinfo, coef_arrays); } jpeg_finish_compress(&cinfo); jpeg_finish_decompress(&dinfo); jpeg_destroy_decompress(&dinfo); jpeg_destroy_compress(&cinfo); return 0; }
static YOPTIMIZE_SPEED int decompress_jpeg(struct jpeg_decompress_struct *cinfo, struct jpeg_compress_struct *cinfoout, JCOPY_OPTION copyoption, Vbitmap *vbitmap, YmagineFormatOptions *options) { int scanlines; int nlines; int totallines; int j; int scalenum = -1; JSAMPARRAY buffer; Vrect srcrect; Vrect destrect; size_t row_stride; Transformer *transformer; PixelShader *shader = NULL; int iwidth, iheight; float sharpen = 0.0f; if (vbitmap == NULL && cinfoout == NULL) { /* No output specified */ return 0; } if (options == NULL) { /* Options argument is mandatory */ return 0; } iwidth = cinfo->image_width; iheight = cinfo->image_height; if (YmaginePrepareTransform(vbitmap, options, iwidth, iheight, &srcrect, &destrect) != YMAGINE_OK) { return 0; } shader = options->pixelshader; sharpen = options->sharpen; /* Define if image can be pre-subsampled by a ratio n/8 (n=1..7) */ scalenum = GetScaleNum(destrect.width, destrect.height, srcrect.width, srcrect.height, options->scalemode); if (scalenum > 0 && scalenum < 8) { cinfo->scale_num = scalenum; cinfo->scale_denom = 8; } /* Compute actual output dimension for image returned by decoder */ jpeg_calc_output_dimensions(cinfo); #if YMAGINE_DEBUG_JPEG ALOGD("src=%dx%d@%d,%d dst=%dx%d@%d,%d", srcrect.width, srcrect.height, srcrect.x, srcrect.y, destrect.width, destrect.height, destrect.x, destrect.y); ALOGD("size: %dx%d req: %dx%d %s -> scale: %d/%d output: %dx%d components: %d", iwidth, iheight, destrect.width, destrect.height, Ymagine_scaleModeStr(options->scalemode), cinfo->scale_num, cinfo->scale_denom, cinfo->output_width, cinfo->output_height, cinfo->output_components); #endif /* Scale the crop region to reflect scaling ratio applied by JPEG decoder */ if (cinfo->image_width != cinfo->output_width) { srcrect.x = (srcrect.x * cinfo->output_width) / cinfo->image_width; srcrect.width = (srcrect.width * cinfo->output_width) / cinfo->image_width; } if (cinfo->image_height != cinfo->output_height) { srcrect.y = (srcrect.y * cinfo->output_height) / cinfo->image_height; srcrect.height = (srcrect.height * cinfo->output_height) / cinfo->image_height; } /* Number of scan lines to handle per pass. Making it larger actually doesn't help much */ row_stride = cinfo->output_width * cinfo->output_components; scanlines = (32 * 1024) / row_stride; if (scanlines < 1) { scanlines = 1; } if (scanlines > cinfo->output_height) { scanlines = cinfo->output_height; } #if YMAGINE_DEBUG_JPEG ALOGD("BITMAP @(%d,%d) %dx%d bpp=%d -> @(%dx%d) %dx%d (%d lines)", srcrect.x, srcrect.y, srcrect.width, srcrect.height, JpegPixelSize(cinfo->out_color_space), destrect.x, destrect.y, destrect.width, destrect.height, scanlines); #endif /* Resize encoder */ if (cinfoout != NULL) { cinfoout->image_width = destrect.width; cinfoout->image_height = destrect.height; jpeg_start_compress(cinfoout, TRUE); if (copyoption != JCOPYOPT_NONE) { /* Copy to the output file any extra markers that we want to preserve */ jcopy_markers_execute(cinfo, cinfoout, copyoption); } } /* Resize target bitmap */ if (vbitmap != NULL) { if (options->resizable) { destrect.x = 0; destrect.y = 0; if (VbitmapResize(vbitmap, destrect.width, destrect.height) != YMAGINE_OK) { return 0; } } if (VbitmapType(vbitmap) == VBITMAP_NONE) { /* Decode bounds only, return positive number (number of lines) on success */ return VbitmapHeight(vbitmap); } } if (!jpeg_start_decompress(cinfo)) { if (cinfoout != NULL) { jpeg_abort_compress(cinfoout); } return 0; } buffer = (JSAMPARRAY) (*cinfo->mem->alloc_sarray)((j_common_ptr) cinfo, JPOOL_IMAGE, row_stride, scanlines); if (buffer == NULL) { if (cinfoout != NULL) { jpeg_abort_compress(cinfoout); } jpeg_abort_decompress(cinfo); return 0; } totallines = 0; transformer = TransformerCreate(); if (transformer != NULL) { TransformerSetScale(transformer, cinfo->output_width, cinfo->output_height, destrect.width, destrect.height); TransformerSetRegion(transformer, srcrect.x, srcrect.y, srcrect.width, srcrect.height); if (vbitmap != NULL) { TransformerSetMode(transformer, JpegPixelMode(cinfo->out_color_space), VbitmapColormode(vbitmap)); TransformerSetBitmap(transformer, vbitmap, destrect.x, destrect.y); } else { TransformerSetMode(transformer, JpegPixelMode(cinfo->out_color_space), JpegPixelMode(cinfoout->in_color_space)); TransformerSetWriter(transformer, JpegWriter, cinfoout); } TransformerSetShader(transformer, shader); TransformerSetSharpen(transformer, sharpen); } while (transformer != NULL && cinfo->output_scanline < cinfo->output_height) { nlines = jpeg_read_scanlines(cinfo, buffer, scanlines); if (nlines <= 0) { /* Decoding error */ ALOGD("decoding error (nlines=%d)", nlines); break; } for (j = 0; j < nlines; j++) { if (TransformerPush(transformer, (const char*) buffer[j]) != YMAGINE_OK) { TransformerRelease(transformer); transformer = NULL; break; } totallines++; } } /* Clean up */ if (transformer != NULL) { TransformerRelease(transformer); } if (cinfo->output_scanline > 0 && cinfo->output_scanline == cinfo->output_height) { /* Do normal cleanup if whole image has been read and decoded */ jpeg_finish_decompress(cinfo); if (cinfoout != NULL) { jpeg_finish_compress(cinfoout); } } else { /* else early abort */ jpeg_abort_decompress(cinfo); if (cinfoout != NULL) { jpeg_abort_compress(cinfoout); } totallines = 0; } return totallines; }
DLLEXPORT int DLLCALL tjCompress(tjhandle h, unsigned char *srcbuf, int width, int pitch, int height, int ps, unsigned char *dstbuf, unsigned long *size, int jpegsub, int qual, int flags) { int i, retval=0; JSAMPROW *row_pointer=NULL; JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS]; JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS]; JSAMPROW *outbuf[MAX_COMPONENTS]; checkhandle(h); for(i=0; i<MAX_COMPONENTS; i++) { tmpbuf[i]=NULL; _tmpbuf[i]=NULL; tmpbuf2[i]=NULL; _tmpbuf2[i]=NULL; outbuf[i]=NULL; } if(srcbuf==NULL || width<=0 || pitch<0 || height<=0 || dstbuf==NULL || size==NULL || jpegsub<0 || jpegsub>=NUMSUBOPT || qual<0 || qual>100) _throw("Invalid argument in tjCompress()"); if(ps!=3 && ps!=4 && ps!=1) _throw("This compressor can only handle 24-bit and 32-bit RGB or 8-bit grayscale input"); if(!j->initc) _throw("Instance has not been initialized for compression"); if(pitch==0) pitch=width*ps; j->cinfo.image_width = width; j->cinfo.image_height = height; j->cinfo.input_components = ps; if(ps==1) j->cinfo.in_color_space = JCS_GRAYSCALE; #if JCS_EXTENSIONS==1 else j->cinfo.in_color_space = JCS_EXT_RGB; if(ps==3 && (flags&TJ_BGR)) j->cinfo.in_color_space = JCS_EXT_BGR; else if(ps==4 && !(flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST)) j->cinfo.in_color_space = JCS_EXT_RGBX; else if(ps==4 && (flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST)) j->cinfo.in_color_space = JCS_EXT_BGRX; else if(ps==4 && (flags&TJ_BGR) && (flags&TJ_ALPHAFIRST)) j->cinfo.in_color_space = JCS_EXT_XBGR; else if(ps==4 && !(flags&TJ_BGR) && (flags&TJ_ALPHAFIRST)) j->cinfo.in_color_space = JCS_EXT_XRGB; #else #error "TurboJPEG requires JPEG colorspace extensions" #endif if(flags&TJ_FORCEMMX) putenv("JSIMD_FORCEMMX=1"); else if(flags&TJ_FORCESSE) putenv("JSIMD_FORCESSE=1"); else if(flags&TJ_FORCESSE2) putenv("JSIMD_FORCESSE2=1"); if(setjmp(j->jerr.jb)) { // this will execute if LIBJPEG has an error retval=-1; goto bailout; } jpeg_set_defaults(&j->cinfo); jpeg_set_quality(&j->cinfo, qual, TRUE); if(jpegsub==TJ_GRAYSCALE) jpeg_set_colorspace(&j->cinfo, JCS_GRAYSCALE); else jpeg_set_colorspace(&j->cinfo, JCS_YCbCr); if(qual>=96) j->cinfo.dct_method=JDCT_ISLOW; else j->cinfo.dct_method=JDCT_FASTEST; j->cinfo.comp_info[0].h_samp_factor=hsampfactor[jpegsub]; j->cinfo.comp_info[1].h_samp_factor=1; j->cinfo.comp_info[2].h_samp_factor=1; j->cinfo.comp_info[0].v_samp_factor=vsampfactor[jpegsub]; j->cinfo.comp_info[1].v_samp_factor=1; j->cinfo.comp_info[2].v_samp_factor=1; j->jdms.next_output_byte = dstbuf; j->jdms.free_in_buffer = TJBUFSIZE(j->cinfo.image_width, j->cinfo.image_height); jpeg_start_compress(&j->cinfo, TRUE); if(flags&TJ_YUV) { j_compress_ptr cinfo=&j->cinfo; int row; int pw=PAD(width, cinfo->max_h_samp_factor); int ph=PAD(height, cinfo->max_v_samp_factor); int cw[MAX_COMPONENTS], ch[MAX_COMPONENTS]; jpeg_component_info *compptr; JSAMPLE *ptr=dstbuf; unsigned long yuvsize=0; if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*ph))==NULL) _throw("Memory allocation failed in tjCompress()"); for(i=0; i<height; i++) { if(flags&TJ_BOTTOMUP) row_pointer[i]= &srcbuf[(height-i-1)*pitch]; else row_pointer[i]= &srcbuf[i*pitch]; } if(height<ph) for(i=height; i<ph; i++) row_pointer[i]=row_pointer[height-1]; for(i=0; i<cinfo->num_components; i++) { compptr=&cinfo->comp_info[i]; _tmpbuf[i]=(JSAMPLE *)malloc( PAD((compptr->width_in_blocks*cinfo->max_h_samp_factor*DCTSIZE) /compptr->h_samp_factor, 16) * cinfo->max_v_samp_factor + 16); if(!_tmpbuf[i]) _throw("Memory allocation failure"); tmpbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*cinfo->max_v_samp_factor); if(!tmpbuf[i]) _throw("Memory allocation failure"); for(row=0; row<cinfo->max_v_samp_factor; row++) { unsigned char *_tmpbuf_aligned= (unsigned char *)PAD((size_t)_tmpbuf[i], 16); tmpbuf[i][row]=&_tmpbuf_aligned[ PAD((compptr->width_in_blocks*cinfo->max_h_samp_factor*DCTSIZE) /compptr->h_samp_factor, 16) * row]; } _tmpbuf2[i]=(JSAMPLE *)malloc(PAD(compptr->width_in_blocks*DCTSIZE, 16) * compptr->v_samp_factor + 16); if(!_tmpbuf2[i]) _throw("Memory allocation failure"); tmpbuf2[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*compptr->v_samp_factor); if(!tmpbuf2[i]) _throw("Memory allocation failure"); for(row=0; row<compptr->v_samp_factor; row++) { unsigned char *_tmpbuf2_aligned= (unsigned char *)PAD((size_t)_tmpbuf2[i], 16); tmpbuf2[i][row]=&_tmpbuf2_aligned[ PAD(compptr->width_in_blocks*DCTSIZE, 16) * row]; } cw[i]=pw*compptr->h_samp_factor/cinfo->max_h_samp_factor; ch[i]=ph*compptr->v_samp_factor/cinfo->max_v_samp_factor; outbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*ch[i]); if(!outbuf[i]) _throw("Memory allocation failure"); for(row=0; row<ch[i]; row++) { outbuf[i][row]=ptr; ptr+=PAD(cw[i], 4); } } yuvsize=(unsigned long)(ptr-dstbuf); for(row=0; row<ph; row+=cinfo->max_v_samp_factor) { (*cinfo->cconvert->color_convert)(cinfo, &row_pointer[row], tmpbuf, 0, cinfo->max_v_samp_factor); (cinfo->downsample->downsample)(cinfo, tmpbuf, 0, tmpbuf2, 0); for(i=0, compptr=cinfo->comp_info; i<cinfo->num_components; i++, compptr++) jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i], row*compptr->v_samp_factor/cinfo->max_v_samp_factor, compptr->v_samp_factor, cw[i]); } *size=yuvsize; cinfo->next_scanline+=height; } else { if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL) _throw("Memory allocation failed in tjCompress()"); for(i=0; i<height; i++) { if(flags&TJ_BOTTOMUP) row_pointer[i]= &srcbuf[(height-i-1)*pitch]; else row_pointer[i]= &srcbuf[i*pitch]; } while(j->cinfo.next_scanline<j->cinfo.image_height) { jpeg_write_scanlines(&j->cinfo, &row_pointer[j->cinfo.next_scanline], j->cinfo.image_height-j->cinfo.next_scanline); } } jpeg_finish_compress(&j->cinfo); if(!(flags&TJ_YUV)) *size=TJBUFSIZE(j->cinfo.image_width, j->cinfo.image_height) -(unsigned long)(j->jdms.free_in_buffer); bailout: if(j->cinfo.global_state>CSTATE_START) jpeg_abort_compress(&j->cinfo); if(row_pointer) free(row_pointer); for(i=0; i<MAX_COMPONENTS; i++) { if(tmpbuf[i]!=NULL) free(tmpbuf[i]); if(_tmpbuf[i]!=NULL) free(_tmpbuf[i]); if(tmpbuf2[i]!=NULL) free(tmpbuf2[i]); if(_tmpbuf2[i]!=NULL) free(_tmpbuf2[i]); if(outbuf[i]!=NULL) free(outbuf[i]); } return retval; }