Skip to content

Instantly share code, notes, and snippets.

@roytam1
Created June 12, 2018 14:59
Show Gist options
  • Save roytam1/5a759a67e94e136ccd41433928630995 to your computer and use it in GitHub Desktop.
Save roytam1/5a759a67e94e136ccd41433928630995 to your computer and use it in GitHub Desktop.
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