Created
June 12, 2018 14:59
-
-
Save roytam1/5a759a67e94e136ccd41433928630995 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/cjpeg.c b/cjpeg.c | |
index 07e7db1..76aba95 100644 | |
--- a/cjpeg.c | |
+++ b/cjpeg.c | |
@@ -6,6 +6,8 @@ | |
* Modified 2003-2011 by Guido Vollbeding. | |
* libjpeg-turbo Modifications: | |
* Copyright (C) 2010, 2013-2014, 2017, D. R. Commander. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* For conditions of distribution and use, see the accompanying README.ijg | |
* file. | |
* | |
@@ -166,14 +168,16 @@ usage(void) | |
fprintf(stderr, " -grayscale Create monochrome JPEG file\n"); | |
fprintf(stderr, " -rgb Create RGB JPEG file\n"); | |
#ifdef ENTROPY_OPT_SUPPORTED | |
- fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n"); | |
+ fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression, enabled by default)\n"); | |
#endif | |
#ifdef C_PROGRESSIVE_SUPPORTED | |
- fprintf(stderr, " -progressive Create progressive JPEG file\n"); | |
+ fprintf(stderr, " -progressive Create progressive JPEG file (enabled by default)\n"); | |
#endif | |
#ifdef TARGA_SUPPORTED | |
fprintf(stderr, " -targa Input file is Targa format (usually not needed)\n"); | |
#endif | |
+ fprintf(stderr, " -revert Revert to standard defaults (instead of mozjpeg defaults)\n"); | |
+ fprintf(stderr, " -fastcrush Disable progressive scan optimization\n"); | |
fprintf(stderr, "Switches for advanced users:\n"); | |
#ifdef C_ARITH_CODING_SUPPORTED | |
fprintf(stderr, " -arithmetic Use arithmetic coding\n"); | |
@@ -239,7 +243,11 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, | |
/* Set up default JPEG parameters. */ | |
force_baseline = FALSE; /* by default, allow 16-bit quantizers */ | |
+#ifdef C_PROGRESSIVE_SUPPORTED | |
+ simple_progressive = cinfo->num_scans == 0 ? FALSE : TRUE; | |
+#else | |
simple_progressive = FALSE; | |
+#endif | |
is_targa = FALSE; | |
icc_filename = NULL; | |
outfilename = NULL; | |
@@ -302,6 +310,9 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, | |
} | |
cinfo->err->trace_level++; | |
+ } else if (keymatch(arg, "fastcrush", 4)) { | |
+ cinfo->optimize_scans = FALSE; | |
+ | |
} else if (keymatch(arg, "version", 4)) { | |
fprintf(stderr, "%s version %s (build %s)\n", | |
PACKAGE_NAME, VERSION, BUILD); | |
@@ -414,6 +425,11 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, | |
/* restart_interval will be computed during startup */ | |
} | |
+ } else if (keymatch(arg, "revert", 3)) { | |
+ /* revert to old JPEG default */ | |
+ cinfo->use_moz_defaults = FALSE; | |
+ jpeg_set_defaults(cinfo); | |
+ | |
} else if (keymatch(arg, "sample", 2)) { | |
/* Set sampling factors. */ | |
if (++argn >= argc) /* advance to next argument */ | |
@@ -543,6 +559,7 @@ main(int argc, char **argv) | |
*/ | |
cinfo.in_color_space = JCS_RGB; /* arbitrary guess */ | |
+ cinfo.use_moz_defaults = TRUE; | |
jpeg_set_defaults(&cinfo); | |
/* Scan command line to find file names. | |
diff --git a/jcapimin.c b/jcapimin.c | |
index 178c55b..7a75bba 100644 | |
--- a/jcapimin.c | |
+++ b/jcapimin.c | |
@@ -92,6 +92,16 @@ jpeg_CreateCompress(j_compress_ptr cinfo, int version, size_t structsize) | |
/* OK, I'm ready */ | |
cinfo->global_state = CSTATE_START; | |
+ | |
+ /* The master struct is used to store extension parameters, so we allocate it | |
+ * here. It is later reallocated by jinit_c_master_control(). | |
+ */ | |
+ cinfo->master = (struct jpeg_comp_master *) | |
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, | |
+ sizeof(struct jpeg_comp_master)); | |
+ MEMZERO(cinfo->master, sizeof(struct jpeg_comp_master)); | |
+ | |
+ cinfo->master->compress_profile = JCP_MAX_COMPRESSION; | |
} | |
diff --git a/jcapistd.c b/jcapistd.c | |
index aa2aad9..3e2490d 100644 | |
--- a/jcapistd.c | |
+++ b/jcapistd.c | |
@@ -3,6 +3,8 @@ | |
* | |
* Copyright (C) 1994-1996, Thomas G. Lane. | |
* This file is part of the Independent JPEG Group's software. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* For conditions of distribution and use, see the accompanying README.ijg | |
* file. | |
* | |
@@ -44,6 +46,10 @@ jpeg_start_compress(j_compress_ptr cinfo, boolean write_all_tables) | |
if (write_all_tables) | |
jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */ | |
+ /* setting up scan optimisation pattern failed, disable scan optimisation */ | |
+ if (cinfo->num_scans_luma == 0) | |
+ cinfo->optimize_scans = FALSE; | |
+ | |
/* (Re)initialize error mgr and destination modules */ | |
(*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo); | |
(*cinfo->dest->init_destination) (cinfo); | |
diff --git a/jcinit.c b/jcinit.c | |
index 78aa465..6bd1a0d 100644 | |
--- a/jcinit.c | |
+++ b/jcinit.c | |
@@ -3,6 +3,8 @@ | |
* | |
* Copyright (C) 1991-1997, Thomas G. Lane. | |
* This file is part of the Independent JPEG Group's software. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* For conditions of distribution and use, see the accompanying README.ijg | |
* file. | |
* | |
@@ -60,8 +62,8 @@ jinit_compress_master(j_compress_ptr cinfo) | |
} | |
/* Need a full-image coefficient buffer in any multi-pass mode. */ | |
- jinit_c_coef_controller(cinfo, (boolean)(cinfo->num_scans > 1 || | |
- cinfo->optimize_coding)); | |
+ jinit_c_coef_controller(cinfo, | |
+ (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding || cinfo->optimize_scans)); | |
jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); | |
jinit_marker_writer(cinfo); | |
diff --git a/jcmarker.c b/jcmarker.c | |
index 801fbab..107a5a6 100644 | |
--- a/jcmarker.c | |
+++ b/jcmarker.c | |
@@ -185,6 +185,74 @@ emit_dqt(j_compress_ptr cinfo, int index) | |
} | |
+LOCAL(int) | |
+emit_multi_dqt (j_compress_ptr cinfo) | |
+/* Emits a DQT marker containing all quantization tables */ | |
+/* Returns number of emitted 16-bit tables, or -1 for failed for baseline checking. */ | |
+{ | |
+ int prec[MAX_COMPONENTS]; | |
+ int seen[MAX_COMPONENTS] = { 0 }; | |
+ int fin_prec = 0; | |
+ int ci; | |
+ int size = 0; | |
+ | |
+ if (cinfo->master->compress_profile == JCP_FASTEST) | |
+ return -1; | |
+ | |
+ for (ci = 0; ci < cinfo->num_components; ci++) { | |
+ int tbl_num = cinfo->comp_info[ci].quant_tbl_no; | |
+ int i; | |
+ JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[tbl_num]; | |
+ | |
+ if (qtbl == NULL || qtbl->sent_table == TRUE) | |
+ return -1; | |
+ | |
+ prec[ci] = 0; | |
+ for (i = 0; i < DCTSIZE2; i++) | |
+ prec[ci] = !!(prec[ci] + (qtbl->quantval[i] > 255)); | |
+ | |
+ fin_prec += prec[ci]; | |
+ } | |
+ | |
+ emit_marker(cinfo, M_DQT); | |
+ | |
+ for (ci = 0; ci < cinfo->num_components; ci++) { | |
+ int tbl_num = cinfo->comp_info[ci].quant_tbl_no; | |
+ | |
+ if (!seen[tbl_num]) { | |
+ size += DCTSIZE2 * (prec[ci] + 1) + 1; | |
+ seen[tbl_num] = 1; | |
+ } | |
+ } | |
+ size += 2; | |
+ | |
+ emit_2bytes(cinfo, size); | |
+ | |
+ for (ci = 0; ci < cinfo->num_components; ci++) { | |
+ int tbl_num = cinfo->comp_info[ci].quant_tbl_no; | |
+ int i; | |
+ JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[tbl_num]; | |
+ | |
+ if (qtbl->sent_table == TRUE) | |
+ continue; | |
+ | |
+ emit_byte(cinfo, tbl_num + (prec[ci] << 4)); | |
+ | |
+ for (i = 0; i < DCTSIZE2; i++) { | |
+ unsigned int qval = qtbl->quantval[jpeg_natural_order[i]]; | |
+ | |
+ if (prec[ci]) | |
+ emit_byte(cinfo, (int) (qval >> 8)); | |
+ emit_byte(cinfo, (int) (qval & 0xFF)); | |
+ } | |
+ | |
+ qtbl->sent_table = TRUE; | |
+ } | |
+ | |
+ return fin_prec; | |
+} | |
+ | |
+ | |
LOCAL(void) | |
emit_dht(j_compress_ptr cinfo, int index, boolean is_ac) | |
/* Emit a DHT marker */ | |
@@ -222,6 +290,113 @@ emit_dht(j_compress_ptr cinfo, int index, boolean is_ac) | |
} | |
} | |
+LOCAL(boolean) | |
+emit_multi_dht (j_compress_ptr cinfo) | |
+/* Emit all DHT markers */ | |
+/* Returns FALSE on failure, TRUE otherwise. */ | |
+{ | |
+ int i, j; | |
+ int length = 2; | |
+ int dclens[NUM_HUFF_TBLS] = { 0 }; | |
+ int aclens[NUM_HUFF_TBLS] = { 0 }; | |
+ JHUFF_TBL *dcseen[NUM_HUFF_TBLS] = { NULL }; | |
+ JHUFF_TBL *acseen[NUM_HUFF_TBLS] = { NULL }; | |
+ | |
+ /* Calclate the total length. */ | |
+ for (i = 0; i < cinfo->comps_in_scan; i++) { | |
+ jpeg_component_info *compptr = cinfo->cur_comp_info[i]; | |
+ int dcidx = compptr->dc_tbl_no; | |
+ int acidx = compptr->ac_tbl_no; | |
+ JHUFF_TBL *dctbl = cinfo->dc_huff_tbl_ptrs[dcidx]; | |
+ JHUFF_TBL *actbl = cinfo->ac_huff_tbl_ptrs[acidx]; | |
+ int seen = 0; | |
+ | |
+ /* Handle DC table lenghts */ | |
+ if (cinfo->Ss == 0 && cinfo->Ah == 0) { | |
+ if (dctbl == NULL) | |
+ ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dcidx); | |
+ | |
+ if (dctbl->sent_table) | |
+ continue; | |
+ | |
+ for (j = 0; j < NUM_HUFF_TBLS; j++) | |
+ seen += (dctbl == dcseen[j]); | |
+ if (seen) | |
+ continue; | |
+ dcseen[i] = dctbl; | |
+ | |
+ for (j = 1; j <= 16; j++) | |
+ dclens[i] += dctbl->bits[j]; | |
+ length += dclens[i] + 16 + 1; | |
+ } | |
+ | |
+ /* Handle AC table lengths */ | |
+ if (cinfo->Se) { | |
+ if (actbl == NULL) | |
+ ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, acidx + 0x10); | |
+ | |
+ if (actbl->sent_table) | |
+ continue; | |
+ | |
+ seen = 0; | |
+ for (j = 0; j < NUM_HUFF_TBLS; j++) | |
+ seen += (actbl == acseen[j]); | |
+ if (seen) | |
+ continue; | |
+ acseen[i] = actbl; | |
+ | |
+ for (j = 1; j <= 16; j++) | |
+ aclens[i] += actbl->bits[j]; | |
+ length += aclens[i] + 16 + 1; | |
+ | |
+ } | |
+ } | |
+ | |
+ /* Make sure we can fit it all into one DHT marker */ | |
+ if (length > (1 << 16) - 1) | |
+ return FALSE; | |
+ | |
+ emit_marker(cinfo, M_DHT); | |
+ emit_2bytes(cinfo, length); | |
+ | |
+ for (i = 0; i < cinfo->comps_in_scan; i++) { | |
+ jpeg_component_info *compptr = cinfo->cur_comp_info[i]; | |
+ int dcidx = compptr->dc_tbl_no; | |
+ int acidx = compptr->ac_tbl_no; | |
+ JHUFF_TBL *dctbl = cinfo->dc_huff_tbl_ptrs[dcidx]; | |
+ JHUFF_TBL *actbl = cinfo->ac_huff_tbl_ptrs[acidx]; | |
+ | |
+ acidx += 0x10; | |
+ | |
+ /* DC */ | |
+ if (cinfo->Ss == 0 && cinfo->Ah == 0 && !dctbl->sent_table) { | |
+ emit_byte(cinfo, dcidx); | |
+ | |
+ for (j = 1; j <= 16; j++) | |
+ emit_byte(cinfo, dctbl->bits[j]); | |
+ | |
+ for (j = 0; j < dclens[i]; j++) | |
+ emit_byte(cinfo, dctbl->huffval[j]); | |
+ | |
+ dctbl->sent_table = TRUE; | |
+ } | |
+ | |
+ if (cinfo->Se && !actbl->sent_table) { | |
+ emit_byte(cinfo, acidx); | |
+ | |
+ for (j = 1; j <= 16; j++) | |
+ emit_byte(cinfo, actbl->bits[j]); | |
+ | |
+ for (j = 0; j < aclens[i]; j++) | |
+ emit_byte(cinfo, actbl->huffval[j]); | |
+ | |
+ actbl->sent_table = TRUE; | |
+ } | |
+ } | |
+ | |
+ return TRUE; | |
+} | |
+ | |
LOCAL(void) | |
emit_dac(j_compress_ptr cinfo) | |
@@ -504,10 +679,13 @@ write_frame_header(j_compress_ptr cinfo) | |
/* Emit DQT for each quantization table. | |
* Note that emit_dqt() suppresses any duplicate tables. | |
*/ | |
- prec = 0; | |
- for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; | |
+ prec = emit_multi_dqt(cinfo); | |
+ if (prec == -1) { | |
+ prec = 0; | |
+ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; | |
ci++, compptr++) { | |
- prec += emit_dqt(cinfo, compptr->quant_tbl_no); | |
+ prec += emit_dqt(cinfo, compptr->quant_tbl_no); | |
+ } | |
} | |
/* now prec is nonzero iff there are any 16-bit quant tables. */ | |
@@ -571,14 +749,16 @@ write_scan_header(j_compress_ptr cinfo) | |
/* Emit Huffman tables. | |
* Note that emit_dht() suppresses any duplicate tables. | |
*/ | |
- for (i = 0; i < cinfo->comps_in_scan; i++) { | |
- compptr = cinfo->cur_comp_info[i]; | |
- /* DC needs no table for refinement scan */ | |
- if (cinfo->Ss == 0 && cinfo->Ah == 0) | |
- emit_dht(cinfo, compptr->dc_tbl_no, FALSE); | |
- /* AC needs no table when not present */ | |
- if (cinfo->Se) | |
- emit_dht(cinfo, compptr->ac_tbl_no, TRUE); | |
+ if (!emit_multi_dht(cinfo)) { | |
+ for (i = 0; i < cinfo->comps_in_scan; i++) { | |
+ compptr = cinfo->cur_comp_info[i]; | |
+ /* DC needs no table for refinement scan */ | |
+ if (cinfo->Ss == 0 && cinfo->Ah == 0) | |
+ emit_dht(cinfo, compptr->dc_tbl_no, FALSE); | |
+ /* AC needs no table when not present */ | |
+ if (cinfo->Se) | |
+ emit_dht(cinfo, compptr->ac_tbl_no, TRUE); | |
+ } | |
} | |
} | |
diff --git a/jcmaster.c b/jcmaster.c | |
index 8516f4b..45f119a 100644 | |
--- a/jcmaster.c | |
+++ b/jcmaster.c | |
@@ -6,6 +6,8 @@ | |
* Modified 2003-2010 by Guido Vollbeding. | |
* libjpeg-turbo Modifications: | |
* Copyright (C) 2010, 2016, D. R. Commander. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* For conditions of distribution and use, see the accompanying README.ijg | |
* file. | |
* | |
@@ -40,6 +42,16 @@ typedef struct { | |
int scan_number; /* current index in scan_info[] */ | |
+ /* fields for scan optimisation */ | |
+ unsigned char * scan_buffer[64]; /* buffer for a given scan */ | |
+ unsigned long scan_size[64]; /* size for a given scan */ | |
+ unsigned long best_cost; /* bit count for best frequency split */ | |
+ int best_freq_split_idx_luma; /* index for best frequency split (luma) */ | |
+ int best_freq_split_idx_chroma; /* index for best frequency split (chroma) */ | |
+ int best_Al_luma; /* best value for Al found in scan search (luma) */ | |
+ int best_Al_chroma; /* best value for Al found in scan search (luma) */ | |
+ boolean interleave_chroma_dc; /* indicate whether to interleave chroma DC scans */ | |
+ struct jpeg_destination_mgr * saved_dest; /* saved value of cinfo->dest */ | |
/* | |
* This is here so we can add libjpeg-turbo version/build information to the | |
* global string table without introducing a new global symbol. Adding this | |
@@ -191,6 +203,14 @@ validate_script(j_compress_ptr cinfo) | |
/* -1 until that coefficient has been seen; then last Al for it */ | |
#endif | |
+ if (cinfo->optimize_scans) { | |
+ cinfo->progressive_mode = TRUE; | |
+ /* When we optimize scans, there is redundancy in the scan list | |
+ * and this function will fail. Therefore skip all this checking | |
+ */ | |
+ return; | |
+ } | |
+ | |
if (cinfo->num_scans <= 0) | |
ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0); | |
@@ -333,6 +353,16 @@ select_scan_parameters(j_compress_ptr cinfo) | |
cinfo->Se = scanptr->Se; | |
cinfo->Ah = scanptr->Ah; | |
cinfo->Al = scanptr->Al; | |
+ if (cinfo->optimize_scans) { | |
+ /* luma frequency split passes */ | |
+ if (master->scan_number >= cinfo->num_scans_luma_dc+3*cinfo->Al_max_luma+2 && | |
+ master->scan_number < cinfo->num_scans_luma) | |
+ cinfo->Al = master->best_Al_luma; | |
+ /* chroma frequency split passes */ | |
+ if (master->scan_number >= cinfo->num_scans_luma+cinfo->num_scans_chroma_dc+(6*cinfo->Al_max_chroma+4) && | |
+ master->scan_number < cinfo->num_scans) | |
+ cinfo->Al = master->best_Al_chroma; | |
+ } | |
} else | |
#endif | |
{ | |
@@ -501,6 +531,13 @@ prepare_for_pass(j_compress_ptr cinfo) | |
select_scan_parameters(cinfo); | |
per_scan_setup(cinfo); | |
} | |
+ if (cinfo->optimize_scans) { | |
+ master->saved_dest = cinfo->dest; | |
+ cinfo->dest = NULL; | |
+ master->scan_size[master->scan_number] = 0; | |
+ jpeg_mem_dest(cinfo, &master->scan_buffer[master->scan_number], &master->scan_size[master->scan_number]); | |
+ (*cinfo->dest->init_destination)(cinfo); | |
+ } | |
(*cinfo->entropy->start_pass) (cinfo, FALSE); | |
(*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); | |
/* We emit frame/scan headers now */ | |
@@ -543,6 +580,210 @@ pass_startup(j_compress_ptr cinfo) | |
} | |
+LOCAL(void) | |
+copy_buffer (j_compress_ptr cinfo, int scan_idx) | |
+{ | |
+ my_master_ptr master = (my_master_ptr) cinfo->master; | |
+ | |
+ unsigned long size = master->scan_size[scan_idx]; | |
+ unsigned char * src = master->scan_buffer[scan_idx]; | |
+ | |
+ while (size >= cinfo->dest->free_in_buffer) | |
+ { | |
+ MEMCOPY(cinfo->dest->next_output_byte, src, cinfo->dest->free_in_buffer); | |
+ src += cinfo->dest->free_in_buffer; | |
+ size -= cinfo->dest->free_in_buffer; | |
+ cinfo->dest->next_output_byte += cinfo->dest->free_in_buffer; | |
+ cinfo->dest->free_in_buffer = 0; | |
+ (*cinfo->dest->empty_output_buffer)(cinfo); | |
+ } | |
+ | |
+ MEMCOPY(cinfo->dest->next_output_byte, src, size); | |
+ cinfo->dest->next_output_byte += size; | |
+ cinfo->dest->free_in_buffer -= size; | |
+} | |
+ | |
+LOCAL(void) | |
+select_scans (j_compress_ptr cinfo, int next_scan_number) | |
+{ | |
+ my_master_ptr master = (my_master_ptr) cinfo->master; | |
+ | |
+ unsigned long size[8]; | |
+ int base_scan_idx; | |
+ int luma_freq_split_scan_start = cinfo->num_scans_luma_dc + 3 * cinfo->Al_max_luma + 2; | |
+ int chroma_freq_split_scan_start = cinfo->num_scans_luma+cinfo->num_scans_chroma_dc+(6*cinfo->Al_max_chroma+4); | |
+ | |
+ if (next_scan_number > 1 && next_scan_number <= luma_freq_split_scan_start) { | |
+ if ((next_scan_number - 1) % 3 == 2) { | |
+ int Al = (next_scan_number - 1) / 3; | |
+ int i; | |
+ unsigned long cost = 0; | |
+ cost += master->scan_size[next_scan_number-2]; | |
+ cost += master->scan_size[next_scan_number-1]; | |
+ for (i = 0; i < Al; i++) | |
+ cost += master->scan_size[3 + 3*i]; | |
+ | |
+ if (Al == 0 || cost < master->best_cost) { | |
+ master->best_cost = cost; | |
+ master->best_Al_luma = Al; | |
+ } else { | |
+ master->scan_number = luma_freq_split_scan_start - 1; | |
+ master->pass_number = 2 * master->scan_number + 1; | |
+ } | |
+ } | |
+ | |
+ } else if (next_scan_number > luma_freq_split_scan_start && next_scan_number <= cinfo->num_scans_luma) { | |
+ if (next_scan_number == luma_freq_split_scan_start + 1) { | |
+ master->best_freq_split_idx_luma = 0; | |
+ master->best_cost = master->scan_size[next_scan_number-1]; | |
+ | |
+ } else if ((next_scan_number - luma_freq_split_scan_start) % 2 == 1) { | |
+ int idx = (next_scan_number - luma_freq_split_scan_start) >> 1; | |
+ unsigned long cost = 0; | |
+ cost += master->scan_size[next_scan_number-2]; | |
+ cost += master->scan_size[next_scan_number-1]; | |
+ | |
+ if (cost < master->best_cost) { | |
+ master->best_cost = cost; | |
+ master->best_freq_split_idx_luma = idx; | |
+ } | |
+ | |
+ /* if after testing first 3, no split is the best, don't search further */ | |
+ if ((idx == 2 && master->best_freq_split_idx_luma == 0) || | |
+ (idx == 3 && master->best_freq_split_idx_luma != 2) || | |
+ (idx == 4 && master->best_freq_split_idx_luma != 4)) { | |
+ master->scan_number = cinfo->num_scans_luma - 1; | |
+ master->pass_number = 2 * master->scan_number + 1; | |
+ master->pub.is_last_pass = (master->pass_number == master->total_passes - 1); | |
+ } | |
+ } | |
+ | |
+ } else if (cinfo->num_scans > cinfo->num_scans_luma) { | |
+ | |
+ if (next_scan_number == cinfo->num_scans_luma+cinfo->num_scans_chroma_dc) { | |
+ base_scan_idx = cinfo->num_scans_luma; | |
+ | |
+ master->interleave_chroma_dc = master->scan_size[base_scan_idx] <= master->scan_size[base_scan_idx+1] + master->scan_size[base_scan_idx+2]; | |
+ | |
+ } else if (next_scan_number > cinfo->num_scans_luma+cinfo->num_scans_chroma_dc && next_scan_number <= chroma_freq_split_scan_start) { | |
+ base_scan_idx = cinfo->num_scans_luma + cinfo->num_scans_chroma_dc; | |
+ if ((next_scan_number - base_scan_idx) % 6 == 4) { | |
+ int Al = (next_scan_number - base_scan_idx) / 6; | |
+ int i; | |
+ unsigned long cost = 0; | |
+ cost += master->scan_size[next_scan_number-4]; | |
+ cost += master->scan_size[next_scan_number-3]; | |
+ cost += master->scan_size[next_scan_number-2]; | |
+ cost += master->scan_size[next_scan_number-1]; | |
+ for (i = 0; i < Al; i++) { | |
+ cost += master->scan_size[base_scan_idx + 4 + 6*i]; | |
+ cost += master->scan_size[base_scan_idx + 5 + 6*i]; | |
+ } | |
+ | |
+ if (Al == 0 || cost < master->best_cost) { | |
+ master->best_cost = cost; | |
+ master->best_Al_chroma = Al; | |
+ } else { | |
+ master->scan_number = chroma_freq_split_scan_start - 1; | |
+ master->pass_number = 2 * master->scan_number + 1; | |
+ } | |
+ } | |
+ | |
+ } else if (next_scan_number > chroma_freq_split_scan_start && next_scan_number <= cinfo->num_scans) { | |
+ if (next_scan_number == chroma_freq_split_scan_start + 2) { | |
+ master->best_freq_split_idx_chroma = 0; | |
+ master->best_cost = master->scan_size[next_scan_number-2]; | |
+ master->best_cost += master->scan_size[next_scan_number-1]; | |
+ | |
+ } else if ((next_scan_number - chroma_freq_split_scan_start) % 4 == 2) { | |
+ int idx = (next_scan_number - chroma_freq_split_scan_start) >> 2; | |
+ unsigned long cost = 0; | |
+ cost += master->scan_size[next_scan_number-4]; | |
+ cost += master->scan_size[next_scan_number-3]; | |
+ cost += master->scan_size[next_scan_number-2]; | |
+ cost += master->scan_size[next_scan_number-1]; | |
+ | |
+ if (cost < master->best_cost) { | |
+ master->best_cost = cost; | |
+ master->best_freq_split_idx_chroma = idx; | |
+ } | |
+ | |
+ /* if after testing first 3, no split is the best, don't search further */ | |
+ if ((idx == 2 && master->best_freq_split_idx_chroma == 0) || | |
+ (idx == 3 && master->best_freq_split_idx_chroma != 2) || | |
+ (idx == 4 && master->best_freq_split_idx_chroma != 4)) { | |
+ master->scan_number = cinfo->num_scans - 1; | |
+ master->pass_number = 2 * master->scan_number + 1; | |
+ master->pub.is_last_pass = (master->pass_number == master->total_passes - 1); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (master->scan_number == cinfo->num_scans - 1) { | |
+ int i, Al; | |
+ int min_Al = MIN(master->best_Al_luma, master->best_Al_chroma); | |
+ | |
+ copy_buffer(cinfo, 0); | |
+ | |
+ if (cinfo->num_scans > cinfo->num_scans_luma) { | |
+ base_scan_idx = cinfo->num_scans_luma; | |
+ | |
+ if (master->interleave_chroma_dc) | |
+ copy_buffer(cinfo, base_scan_idx); | |
+ else { | |
+ copy_buffer(cinfo, base_scan_idx+1); | |
+ copy_buffer(cinfo, base_scan_idx+2); | |
+ } | |
+ } | |
+ | |
+ if (master->best_freq_split_idx_luma == 0) | |
+ copy_buffer(cinfo, luma_freq_split_scan_start); | |
+ else { | |
+ copy_buffer(cinfo, luma_freq_split_scan_start+2*(master->best_freq_split_idx_luma-1)+1); | |
+ copy_buffer(cinfo, luma_freq_split_scan_start+2*(master->best_freq_split_idx_luma-1)+2); | |
+ } | |
+ | |
+ /* copy the LSB refinements as well */ | |
+ for (Al = master->best_Al_luma-1; Al >= min_Al; Al--) | |
+ copy_buffer(cinfo, 3 + 3*Al); | |
+ | |
+ if (cinfo->num_scans > cinfo->num_scans_luma) { | |
+ if (master->best_freq_split_idx_chroma == 0) { | |
+ copy_buffer(cinfo, chroma_freq_split_scan_start); | |
+ copy_buffer(cinfo, chroma_freq_split_scan_start+1); | |
+ } | |
+ else { | |
+ copy_buffer(cinfo, chroma_freq_split_scan_start+4*(master->best_freq_split_idx_chroma-1)+2); | |
+ copy_buffer(cinfo, chroma_freq_split_scan_start+4*(master->best_freq_split_idx_chroma-1)+3); | |
+ copy_buffer(cinfo, chroma_freq_split_scan_start+4*(master->best_freq_split_idx_chroma-1)+4); | |
+ copy_buffer(cinfo, chroma_freq_split_scan_start+4*(master->best_freq_split_idx_chroma-1)+5); | |
+ } | |
+ | |
+ base_scan_idx = cinfo->num_scans_luma + cinfo->num_scans_chroma_dc; | |
+ | |
+ for (Al = master->best_Al_chroma-1; Al >= min_Al; Al--) { | |
+ copy_buffer(cinfo, base_scan_idx + 6*Al + 4); | |
+ copy_buffer(cinfo, base_scan_idx + 6*Al + 5); | |
+ } | |
+ } | |
+ | |
+ for (Al = min_Al-1; Al >= 0; Al--) { | |
+ copy_buffer(cinfo, 3 + 3*Al); | |
+ | |
+ if (cinfo->num_scans > cinfo->num_scans_luma) { | |
+ copy_buffer(cinfo, base_scan_idx + 6*Al + 4); | |
+ copy_buffer(cinfo, base_scan_idx + 6*Al + 5); | |
+ } | |
+ } | |
+ | |
+ /* free the memory allocated for buffers */ | |
+ for (i = 0; i < cinfo->num_scans; i++) | |
+ if (master->scan_buffer[i]) | |
+ free(master->scan_buffer[i]); | |
+ } | |
+} | |
+ | |
/* | |
* Finish up at end of pass. | |
*/ | |
@@ -575,6 +816,12 @@ finish_pass_master(j_compress_ptr cinfo) | |
/* next pass is either optimization or output of next scan */ | |
if (cinfo->optimize_coding) | |
master->pass_type = huff_opt_pass; | |
+ if (cinfo->optimize_scans) { | |
+ (*cinfo->dest->term_destination)(cinfo); | |
+ cinfo->dest = master->saved_dest; | |
+ select_scans(cinfo, master->scan_number + 1); | |
+ } | |
+ | |
master->scan_number++; | |
break; | |
} | |
@@ -636,5 +883,13 @@ jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only) | |
else | |
master->total_passes = cinfo->num_scans; | |
+ if (cinfo->optimize_scans) { | |
+ int i; | |
+ master->best_Al_chroma = 0; | |
+ | |
+ for (i = 0; i < cinfo->num_scans; i++) | |
+ master->scan_buffer[i] = NULL; | |
+ } | |
+ | |
master->jpeg_version = PACKAGE_NAME " version " VERSION " (build " BUILD ")"; | |
} | |
diff --git a/jcparam.c b/jcparam.c | |
index bcea927..7a12caf 100644 | |
--- a/jcparam.c | |
+++ b/jcparam.c | |
@@ -6,6 +6,8 @@ | |
* Modified 2003-2008 by Guido Vollbeding. | |
* libjpeg-turbo Modifications: | |
* Copyright (C) 2009-2011, D. R. Commander. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* For conditions of distribution and use, see the accompanying README.ijg | |
* file. | |
* | |
@@ -214,18 +216,39 @@ jpeg_set_defaults(j_compress_ptr cinfo) | |
cinfo->arith_ac_K[i] = 5; | |
} | |
+#ifdef C_PROGRESSIVE_SUPPORTED | |
+ cinfo->optimize_scans = TRUE; | |
+ if (cinfo->use_moz_defaults) | |
+ jpeg_simple_progression(cinfo); | |
+ else { | |
+ /* Default is no multiple-scan output */ | |
+ cinfo->scan_info = NULL; | |
+ cinfo->num_scans = 0; | |
+ } | |
+#else | |
/* Default is no multiple-scan output */ | |
cinfo->scan_info = NULL; | |
cinfo->num_scans = 0; | |
- | |
+#endif | |
+ | |
/* Expect normal source image, not raw downsampled data */ | |
cinfo->raw_data_in = FALSE; | |
/* Use Huffman coding, not arithmetic coding, by default */ | |
cinfo->arith_code = FALSE; | |
+#ifdef ENTROPY_OPT_SUPPORTED | |
+ if (cinfo->use_moz_defaults) | |
+ /* By default, do extra passes to optimize entropy coding */ | |
+ cinfo->optimize_coding = TRUE; | |
+ else | |
+ /* By default, don't do extra passes to optimize entropy coding */ | |
+ cinfo->optimize_coding = FALSE; | |
+#else | |
/* By default, don't do extra passes to optimize entropy coding */ | |
cinfo->optimize_coding = FALSE; | |
+#endif | |
+ | |
/* The standard Huffman tables are only valid for 8-bit data precision. | |
* If the precision is higher, force optimization on so that usable | |
* tables will be computed. This test can be removed if default tables | |
@@ -417,6 +440,22 @@ fill_a_scan(jpeg_scan_info *scanptr, int ci, int Ss, int Se, int Ah, int Al) | |
return scanptr; | |
} | |
+LOCAL(jpeg_scan_info *) | |
+fill_a_scan_pair (jpeg_scan_info * scanptr, int ci, | |
+ int Ss, int Se, int Ah, int Al) | |
+/* Support routine: generate one scan for pair of components */ | |
+{ | |
+ scanptr->comps_in_scan = 2; | |
+ scanptr->component_index[0] = ci; | |
+ scanptr->component_index[1] = ci + 1; | |
+ scanptr->Ss = Ss; | |
+ scanptr->Se = Se; | |
+ scanptr->Ah = Ah; | |
+ scanptr->Al = Al; | |
+ scanptr++; | |
+ return scanptr; | |
+} | |
+ | |
LOCAL(jpeg_scan_info *) | |
fill_scans(jpeg_scan_info *scanptr, int ncomps, int Ss, int Se, int Ah, int Al) | |
/* Support routine: generate one scan for each component */ | |
@@ -458,6 +497,127 @@ fill_dc_scans(jpeg_scan_info *scanptr, int ncomps, int Ah, int Al) | |
} | |
+/* | |
+ * List of scans to be tested | |
+ * cinfo->num_components and cinfo->jpeg_color_space must be correct. | |
+ */ | |
+ | |
+LOCAL(boolean) | |
+jpeg_search_progression (j_compress_ptr cinfo) | |
+{ | |
+ int ncomps = cinfo->num_components; | |
+ int nscans; | |
+ jpeg_scan_info * scanptr; | |
+ int Al; | |
+ int frequency_split[] = { 2, 8, 5, 12, 18 }; | |
+ int i; | |
+ | |
+ /* Safety check to ensure start_compress not called yet. */ | |
+ if (cinfo->global_state != CSTATE_START) | |
+ ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); | |
+ | |
+ /* Figure space needed for script. Calculation must match code below! */ | |
+ if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { | |
+ /* Custom script for YCbCr color images. */ | |
+ nscans = 64; | |
+ } else if (ncomps == 1) { | |
+ nscans = 23; | |
+ } else { | |
+ cinfo->num_scans_luma = 0; | |
+ return FALSE; | |
+ } | |
+ | |
+ /* Allocate space for script. | |
+ * We need to put it in the permanent pool in case the application performs | |
+ * multiple compressions without changing the settings. To avoid a memory | |
+ * leak if jpeg_simple_progression is called repeatedly for the same JPEG | |
+ * object, we try to re-use previously allocated space, and we allocate | |
+ * enough space to handle YCbCr even if initially asked for grayscale. | |
+ */ | |
+ if (cinfo->script_space == NULL || cinfo->script_space_size < nscans) { | |
+ cinfo->script_space_size = MAX(nscans, 64); | |
+ cinfo->script_space = (jpeg_scan_info *) | |
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, | |
+ cinfo->script_space_size * sizeof(jpeg_scan_info)); | |
+ } | |
+ scanptr = cinfo->script_space; | |
+ cinfo->scan_info = scanptr; | |
+ cinfo->num_scans = nscans; | |
+ | |
+ cinfo->Al_max_luma = 3; | |
+ cinfo->num_scans_luma_dc = 1; | |
+ cinfo->num_frequency_splits = 5; | |
+ cinfo->num_scans_luma = cinfo->num_scans_luma_dc + (3 * cinfo->Al_max_luma + 2) + (2 * cinfo->num_frequency_splits + 1); | |
+ | |
+ /* 23 scans for luma */ | |
+ /* 1 scan for DC */ | |
+ /* 11 scans to determine successive approximation */ | |
+ /* 11 scans to determine frequency approximation */ | |
+ /* after 12 scans need to update following 11 */ | |
+ /* after 23 scans need to determine which to keep */ | |
+ /* last 4 done conditionally */ | |
+ | |
+ /* luma DC by itself */ | |
+ scanptr = fill_dc_scans(scanptr, 1, 0, 0); | |
+ | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 8, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 0, 9, 63, 0, 0); | |
+ | |
+ for (Al = 0; Al < cinfo->Al_max_luma; Al++) { | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 63, Al+1, Al); | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 8, 0, Al+1); | |
+ scanptr = fill_a_scan(scanptr, 0, 9, 63, 0, Al+1); | |
+ } | |
+ | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 63, 0, 0); | |
+ | |
+ for (i = 0; i < cinfo->num_frequency_splits; i++) { | |
+ scanptr = fill_a_scan(scanptr, 0, 1, frequency_split[i], 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 0, frequency_split[i]+1, 63, 0, 0); | |
+ } | |
+ | |
+ if (ncomps == 1) { | |
+ cinfo->Al_max_chroma = 0; | |
+ cinfo->num_scans_chroma_dc = 0; | |
+ } else { | |
+ cinfo->Al_max_chroma = 2; | |
+ cinfo->num_scans_chroma_dc = 3; | |
+ /* 41 scans for chroma */ | |
+ | |
+ /* chroma DC combined */ | |
+ scanptr = fill_a_scan_pair(scanptr, 1, 0, 0, 0, 0); | |
+ /* chroma DC separate */ | |
+ scanptr = fill_a_scan(scanptr, 1, 0, 0, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, 0, 0, 0, 0); | |
+ | |
+ scanptr = fill_a_scan(scanptr, 1, 1, 8, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 1, 9, 63, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, 1, 8, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, 9, 63, 0, 0); | |
+ | |
+ for (Al = 0; Al < cinfo->Al_max_chroma; Al++) { | |
+ scanptr = fill_a_scan(scanptr, 1, 1, 63, Al+1, Al); | |
+ scanptr = fill_a_scan(scanptr, 2, 1, 63, Al+1, Al); | |
+ scanptr = fill_a_scan(scanptr, 1, 1, 8, 0, Al+1); | |
+ scanptr = fill_a_scan(scanptr, 1, 9, 63, 0, Al+1); | |
+ scanptr = fill_a_scan(scanptr, 2, 1, 8, 0, Al+1); | |
+ scanptr = fill_a_scan(scanptr, 2, 9, 63, 0, Al+1); | |
+ } | |
+ | |
+ scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 0); | |
+ | |
+ for (i = 0; i < cinfo->num_frequency_splits; i++) { | |
+ scanptr = fill_a_scan(scanptr, 1, 1, frequency_split[i], 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 1, frequency_split[i]+1, 63, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, 1, frequency_split[i], 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, frequency_split[i]+1, 63, 0, 0); | |
+ } | |
+ } | |
+ | |
+ return TRUE; | |
+} | |
+ | |
/* | |
* Create a recommended progressive-JPEG script. | |
* cinfo->num_components and cinfo->jpeg_color_space must be correct. | |
@@ -466,6 +626,11 @@ fill_dc_scans(jpeg_scan_info *scanptr, int ncomps, int Ah, int Al) | |
GLOBAL(void) | |
jpeg_simple_progression(j_compress_ptr cinfo) | |
{ | |
+ if (cinfo->optimize_scans) { | |
+ if (jpeg_search_progression(cinfo) == TRUE) | |
+ return; | |
+ } | |
+ | |
int ncomps = cinfo->num_components; | |
int nscans; | |
jpeg_scan_info *scanptr; | |
@@ -474,16 +639,34 @@ jpeg_simple_progression(j_compress_ptr cinfo) | |
if (cinfo->global_state != CSTATE_START) | |
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); | |
- /* Figure space needed for script. Calculation must match code below! */ | |
+ /* Figure space needed for script. Calculation must match code below! */ | |
+ ncomps = cinfo->num_components; | |
if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { | |
/* Custom script for YCbCr color images. */ | |
- nscans = 10; | |
+ if (cinfo->master->compress_profile == JCP_MAX_COMPRESSION) { | |
+ if (cinfo->master->dc_scan_opt_mode == 0) { | |
+ nscans = 9; /* 1 DC scan for all components */ | |
+ } else if (cinfo->master->dc_scan_opt_mode == 1) { | |
+ nscans = 11; /* 1 DC scan for each component */ | |
+ } else { | |
+ nscans = 10; /* 1 DC scan for luminance and 1 DC scan for chroma */ | |
+ } | |
+ } else { | |
+ nscans = 10; /* 2 DC scans and 8 AC scans */ | |
+ } | |
} else { | |
/* All-purpose script for other color spaces. */ | |
- if (ncomps > MAX_COMPS_IN_SCAN) | |
- nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ | |
- else | |
- nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ | |
+ if (cinfo->master->compress_profile == JCP_MAX_COMPRESSION) { | |
+ if (ncomps > MAX_COMPS_IN_SCAN) | |
+ nscans = 5 * ncomps; /* 2 DC + 4 AC scans per component */ | |
+ else | |
+ nscans = 1 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ | |
+ } else { | |
+ if (ncomps > MAX_COMPS_IN_SCAN) | |
+ nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ | |
+ else | |
+ nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ | |
+ } | |
} | |
/* Allocate space for script. | |
@@ -505,36 +688,80 @@ jpeg_simple_progression(j_compress_ptr cinfo) | |
if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { | |
/* Custom script for YCbCr color images. */ | |
- /* Initial DC scan */ | |
- scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); | |
- /* Initial AC scan: get some luma data out in a hurry */ | |
- scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); | |
- /* Chroma data is too small to be worth expending many scans on */ | |
- scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); | |
- scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); | |
- /* Complete spectral selection for luma AC */ | |
- scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); | |
- /* Refine next bit of luma AC */ | |
- scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); | |
- /* Finish DC successive approximation */ | |
- scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); | |
- /* Finish AC successive approximation */ | |
- scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); | |
- scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); | |
- /* Luma bottom bit comes last since it's usually largest scan */ | |
- scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); | |
+ if (cinfo->master->compress_profile == JCP_MAX_COMPRESSION) { | |
+ /* scan defined in jpeg_scan_rgb.txt in jpgcrush */ | |
+ /* Initial DC scan */ | |
+ if (cinfo->master->dc_scan_opt_mode == 0) { | |
+ /* 1 DC scan for all components */ | |
+ scanptr = fill_dc_scans(scanptr, ncomps, 0, 0); | |
+ } else if (cinfo->master->dc_scan_opt_mode == 1) { | |
+ /* 1 DC scan for each component */ | |
+ scanptr = fill_a_scan(scanptr, 0, 0, 0, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 1, 0, 0, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, 0, 0, 0, 0); | |
+ } else { | |
+ /* 1 DC scan for luminance and 1 DC scan for chroma */ | |
+ scanptr = fill_dc_scans(scanptr, 1, 0, 0); | |
+ scanptr = fill_a_scan_pair(scanptr, 1, 0, 0, 0, 0); | |
+ } | |
+ /* Low frequency AC scans */ | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 8, 0, 2); | |
+ scanptr = fill_a_scan(scanptr, 1, 1, 8, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, 1, 8, 0, 0); | |
+ /* Complete spectral selection for luma AC */ | |
+ scanptr = fill_a_scan(scanptr, 0, 9, 63, 0, 2); | |
+ /* Finish luma AC successive approximation */ | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); | |
+ /* Complete spectral selection for chroma AC */ | |
+ scanptr = fill_a_scan(scanptr, 1, 9, 63, 0, 0); | |
+ scanptr = fill_a_scan(scanptr, 2, 9, 63, 0, 0); | |
+ } else { | |
+ /* Initial DC scan */ | |
+ scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); | |
+ /* Initial AC scan: get some luma data out in a hurry */ | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); | |
+ /* Chroma data is too small to be worth expending many scans on */ | |
+ scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); | |
+ scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); | |
+ /* Complete spectral selection for luma AC */ | |
+ scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); | |
+ /* Refine next bit of luma AC */ | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); | |
+ /* Finish DC successive approximation */ | |
+ scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); | |
+ /* Finish AC successive approximation */ | |
+ scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); | |
+ scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); | |
+ /* Luma bottom bit comes last since it's usually largest scan */ | |
+ scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); | |
+ } | |
} else { | |
/* All-purpose script for other color spaces. */ | |
- /* Successive approximation first pass */ | |
- scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); | |
- scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); | |
- scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); | |
- /* Successive approximation second pass */ | |
- scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); | |
- /* Successive approximation final pass */ | |
- scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); | |
- scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); | |
+ if (cinfo->master->compress_profile == JCP_MAX_COMPRESSION) { | |
+ /* scan defined in jpeg_scan_bw.txt in jpgcrush */ | |
+ /* DC component, no successive approximation */ | |
+ scanptr = fill_dc_scans(scanptr, ncomps, 0, 0); | |
+ /* Successive approximation first pass */ | |
+ scanptr = fill_scans(scanptr, ncomps, 1, 8, 0, 2); | |
+ scanptr = fill_scans(scanptr, ncomps, 9, 63, 0, 2); | |
+ /* Successive approximation second pass */ | |
+ scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); | |
+ /* Successive approximation final pass */ | |
+ scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); | |
+ } else { | |
+ /* Successive approximation first pass */ | |
+ scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); | |
+ scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); | |
+ scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); | |
+ /* Successive approximation second pass */ | |
+ scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); | |
+ /* Successive approximation final pass */ | |
+ scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); | |
+ scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); | |
+ } | |
} | |
} | |
+ | |
#endif /* C_PROGRESSIVE_SUPPORTED */ | |
diff --git a/jcstest.c b/jcstest.c | |
index 8b1fe38..c602430 100644 | |
--- a/jcstest.c | |
+++ b/jcstest.c | |
@@ -1,5 +1,7 @@ | |
/* | |
* Copyright (C)2011 D. R. Commander. All Rights Reserved. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
@@ -85,6 +87,7 @@ int main(void) | |
jpeg_create_compress(&cinfo); | |
cinfo.input_components = 3; | |
+ cinfo.use_moz_defaults = TRUE; | |
jpeg_set_defaults(&cinfo); | |
cinfo.in_color_space = JCS_EXT_RGB; | |
jpeg_default_colorspace(&cinfo); | |
diff --git a/jpegint.h b/jpegint.h | |
index ad36ca8..359a8c6 100644 | |
--- a/jpegint.h | |
+++ b/jpegint.h | |
@@ -67,6 +67,26 @@ struct jpeg_comp_master { | |
/* State variables made visible to other modules */ | |
boolean call_pass_startup; /* True if pass_startup must be called */ | |
boolean is_last_pass; /* True during last pass */ | |
+ | |
+ /* Extension parameters */ | |
+ int compress_profile; /* compression profile */ | |
+ boolean use_moz_defaults; /* TRUE=use Mozilla defaults */ | |
+ boolean optimize_scans; /* TRUE=optimize progressive coding scans */ | |
+ int dc_scan_opt_mode; /* DC scan optimization mode */ | |
+ int num_scans_luma; /* # of entries in scan_info array pertaining to luma (used when optimize_scans is TRUE */ | |
+ int num_scans_luma_dc; | |
+ int num_scans_chroma_dc; | |
+ int num_frequency_splits; | |
+ | |
+ int Al_max_luma; /* maximum value of Al tested when optimizing scans (luma) */ | |
+ int Al_max_chroma; /* maximum value of Al tested when optimizing scans (chroma) */ | |
+}; | |
+ | |
+/* Values for the JINT_COMPRESS_PROFILE parameter (32-bit GUIDs) */ | |
+ | |
+enum { | |
+ JCP_MAX_COMPRESSION = 0x5D083AAD, /* best compression ratio (progressive, all mozjpeg extensions) */ | |
+ JCP_FASTEST = 0x2AEA5CB4 /* libjpeg[-turbo] defaults (baseline, no mozjpeg extensions) */ | |
}; | |
/* Main buffer control (downsampled-data buffer) */ | |
diff --git a/jpeglib.h b/jpeglib.h | |
index 33f8ad2..be0d240 100644 | |
--- a/jpeglib.h | |
+++ b/jpeglib.h | |
@@ -7,6 +7,8 @@ | |
* libjpeg-turbo Modifications: | |
* Copyright (C) 2009-2011, 2013-2014, 2016-2017, D. R. Commander. | |
* Copyright (C) 2015, Google, Inc. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* For conditions of distribution and use, see the accompanying README.ijg | |
* file. | |
* | |
@@ -374,6 +376,17 @@ struct jpeg_compress_struct { | |
int smoothing_factor; /* 1..100, or 0 for no input smoothing */ | |
J_DCT_METHOD dct_method; /* DCT algorithm selector */ | |
+ boolean use_moz_defaults; /* TRUE if using Mozilla defaults */ | |
+ boolean optimize_scans; /* TRUE=optimize progressive coding scans */ | |
+ | |
+ int num_scans_luma; /* # of entries in scan_info array pertaining to luma (used when optimize_scans is TRUE */ | |
+ int num_scans_luma_dc; | |
+ int num_scans_chroma_dc; | |
+ int num_frequency_splits; | |
+ | |
+ int Al_max_luma; /* maximum value of Al tested when optimizing scans (luma) */ | |
+ int Al_max_chroma; /* maximum value of Al tested when optimizing scans (chroma) */ | |
+ | |
/* The restart interval can be specified in absolute MCUs by setting | |
* restart_interval, or in MCU rows by setting restart_in_rows | |
* (in which case the correct restart_interval will be figured | |
diff --git a/jpegtran.c b/jpegtran.c | |
index 058e844..8f0d7e4 100644 | |
--- a/jpegtran.c | |
+++ b/jpegtran.c | |
@@ -5,6 +5,8 @@ | |
* Copyright (C) 1995-2010, Thomas G. Lane, Guido Vollbeding. | |
* libjpeg-turbo Modifications: | |
* Copyright (C) 2010, 2014, 2017, D. R. Commander. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* For conditions of distribution and use, see the accompanying README.ijg | |
* file. | |
* | |
@@ -44,6 +46,8 @@ static char *icc_filename; /* for -icc switch */ | |
static char *outfilename; /* for -outfile switch */ | |
static JCOPY_OPTION copyoption; /* -copy switch */ | |
static jpeg_transform_info transformoption; /* image transformation options */ | |
+boolean memsrc; /* for -memsrc switch */ | |
+#define INPUT_BUF_SIZE 4096 | |
LOCAL(void) | |
@@ -62,11 +66,13 @@ usage(void) | |
fprintf(stderr, " -copy comments Copy only comment markers (default)\n"); | |
fprintf(stderr, " -copy all Copy all extra markers\n"); | |
#ifdef ENTROPY_OPT_SUPPORTED | |
- fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n"); | |
+ fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression, enabled by default)\n"); | |
#endif | |
#ifdef C_PROGRESSIVE_SUPPORTED | |
- fprintf(stderr, " -progressive Create progressive JPEG file\n"); | |
+ fprintf(stderr, " -progressive Create progressive JPEG file (enabled by default)\n"); | |
#endif | |
+ fprintf(stderr, " -revert Revert to standard defaults (instead of mozjpeg defaults)\n"); | |
+ fprintf(stderr, " -fastcrush Disable progressive scan optimization\n"); | |
fprintf(stderr, "Switches for modifying the image:\n"); | |
#if TRANSFORMS_SUPPORTED | |
fprintf(stderr, " -crop WxH+X+Y Crop to a rectangular subarea\n"); | |
@@ -139,7 +145,11 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, | |
char *scansarg = NULL; /* saves -scans parm if any */ | |
/* Set up default JPEG parameters. */ | |
+#ifdef C_PROGRESSIVE_SUPPORTED | |
+ simple_progressive = cinfo->num_scans == 0 ? FALSE : TRUE; | |
+#else | |
simple_progressive = FALSE; | |
+#endif | |
icc_filename = NULL; | |
outfilename = NULL; | |
copyoption = JCOPYOPT_DEFAULT; | |
@@ -233,6 +243,9 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, | |
else | |
usage(); | |
+ } else if (keymatch(arg, "fastcrush", 4)) { | |
+ cinfo->optimize_scans = FALSE; | |
+ | |
} else if (keymatch(arg, "grayscale", 1) || | |
keymatch(arg, "greyscale", 1)) { | |
/* Force to grayscale. */ | |
@@ -312,6 +325,10 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, | |
/* restart_interval will be computed during startup */ | |
} | |
+ } else if (keymatch(arg, "revert", 3)) { | |
+ /* revert to old JPEG default */ | |
+ cinfo->use_moz_defaults = FALSE; | |
+ | |
} else if (keymatch(arg, "rotate", 2)) { | |
/* Rotate 90, 180, or 270 degrees (measured clockwise). */ | |
if (++argn >= argc) /* advance to next argument */ | |
@@ -399,6 +416,11 @@ main(int argc, char **argv) | |
JOCTET *icc_profile = NULL; | |
long icc_len = 0; | |
+ unsigned char *inbuffer = NULL; | |
+ unsigned long insize = 0; | |
+ unsigned char *outbuffer = NULL; | |
+ unsigned long outsize = 0; | |
+ | |
/* On Mac, fetch a command line. */ | |
#ifdef USE_CCOMMAND | |
argc = ccommand(&argv); | |
@@ -414,6 +436,7 @@ main(int argc, char **argv) | |
/* Initialize the JPEG compression object with default error handling. */ | |
dstinfo.err = jpeg_std_error(&jdsterr); | |
jpeg_create_compress(&dstinfo); | |
+ dstinfo.use_moz_defaults = TRUE; | |
/* Scan command line to find file names. | |
* It is convenient to use just one switch-parsing routine, but the switch | |
@@ -497,7 +520,30 @@ main(int argc, char **argv) | |
#endif | |
/* Specify data source for decompression */ | |
- jpeg_stdio_src(&srcinfo, fp); | |
+ memsrc = TRUE; /* needed to revert to original */ | |
+#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) | |
+ if (memsrc) { | |
+ size_t nbytes; | |
+ do { | |
+ inbuffer = (unsigned char *)realloc(inbuffer, insize + INPUT_BUF_SIZE); | |
+ if (inbuffer == NULL) { | |
+ fprintf(stderr, "%s: memory allocation failure\n", progname); | |
+ exit(EXIT_FAILURE); | |
+ } | |
+ nbytes = JFREAD(fp, &inbuffer[insize], INPUT_BUF_SIZE); | |
+ if (nbytes < INPUT_BUF_SIZE && ferror(fp)) { | |
+ if (file_index < argc) | |
+ fprintf(stderr, "%s: can't read from %s\n", progname, | |
+ argv[file_index]); | |
+ else | |
+ fprintf(stderr, "%s: can't read from stdin\n", progname); | |
+ } | |
+ insize += (unsigned long)nbytes; | |
+ } while (nbytes == INPUT_BUF_SIZE); | |
+ jpeg_mem_src(&srcinfo, inbuffer, insize); | |
+ } else | |
+#endif | |
+ jpeg_stdio_src(&srcinfo, fp); | |
/* Enable saving of extra markers that we want to copy */ | |
jcopy_markers_setup(&srcinfo, copyoption); | |
@@ -560,7 +606,12 @@ main(int argc, char **argv) | |
file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE); | |
/* Specify data destination for compression */ | |
- jpeg_stdio_dest(&dstinfo, fp); | |
+#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) | |
+ if (dstinfo.use_moz_defaults) | |
+ jpeg_mem_dest(&dstinfo, &outbuffer, &outsize); | |
+ else | |
+#endif | |
+ jpeg_stdio_dest(&dstinfo, fp); | |
/* Start compressor (note no image data is actually written here) */ | |
jpeg_write_coefficients(&dstinfo, dst_coef_arrays); | |
@@ -579,6 +630,27 @@ main(int argc, char **argv) | |
/* Finish compression and release memory */ | |
jpeg_finish_compress(&dstinfo); | |
+ | |
+ if (dstinfo.use_moz_defaults) { | |
+ size_t nbytes; | |
+ | |
+ unsigned char *buffer = outbuffer; | |
+ unsigned long size = outsize; | |
+ if (insize < size) { | |
+ size = insize; | |
+ buffer = inbuffer; | |
+ } | |
+ | |
+ nbytes = JFWRITE(fp, buffer, size); | |
+ if (nbytes < size && ferror(fp)) { | |
+ if (file_index < argc) | |
+ fprintf(stderr, "%s: can't write to %s\n", progname, | |
+ argv[file_index]); | |
+ else | |
+ fprintf(stderr, "%s: can't write to stdout\n", progname); | |
+ } | |
+ } | |
+ | |
jpeg_destroy_compress(&dstinfo); | |
(void)jpeg_finish_decompress(&srcinfo); | |
jpeg_destroy_decompress(&srcinfo); | |
diff --git a/turbojpeg.c b/turbojpeg.c | |
index a41e8df..49191b8 100644 | |
--- a/turbojpeg.c | |
+++ b/turbojpeg.c | |
@@ -1,5 +1,7 @@ | |
/* | |
* Copyright (C)2009-2018 D. R. Commander. All Rights Reserved. | |
+ * mozjpeg Modifications: | |
+ * Copyright (C) 2014, Mozilla Corporation. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
@@ -235,6 +237,7 @@ static int setCompDefaults(struct jpeg_compress_struct *cinfo, int pixelFormat, | |
cinfo->in_color_space = pf2cs[pixelFormat]; | |
cinfo->input_components = tjPixelSize[pixelFormat]; | |
+ cinfo->use_moz_defaults = TRUE; | |
jpeg_set_defaults(cinfo); | |
#ifndef NO_GETENV |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment