2 # Copyright 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 # The optimization code is based on pngslim (http://goo.gl/a0XHg)
7 # and executes a similar pipleline to optimize the png file size.
8 # The steps that require pngoptimizercl/pngrewrite/deflopt are omitted,
9 # but this runs all other processes, including:
10 # 1) various color-dependent optimizations using optipng.
11 # 2) optimize the number of huffman blocks.
12 # 3) randomize the huffman table.
13 # 4) Further optimize using optipng and advdef (zlib stream).
14 # Due to the step 3), each run may produce slightly different results.
16 # Note(oshima): In my experiment, advdef didn't reduce much. I'm keeping it
17 # for now as it does not take much time to run.
21 chrome/android/java/res
23 chrome/browser/resources
24 chrome/renderer/resources
26 content/public/android/java/res
28 content/renderer/resources
29 content/shell/resources
34 ui/webui/resources/images
35 win8/metro_driver/resources
38 # Files larger than this file size (in bytes) will
39 # use the optimization parameters tailored for large files.
40 LARGE_FILE_THRESHOLD
=3000
42 # Constants used for optimization
43 readonly DEFAULT_MIN_BLOCK_SIZE
=128
44 readonly DEFAULT_LIMIT_BLOCKS
=256
45 readonly DEFAULT_RANDOM_TRIALS
=100
46 # Taken from the recommendation in the pngslim's readme.txt.
47 readonly LARGE_MIN_BLOCK_SIZE
=1
48 readonly LARGE_LIMIT_BLOCKS
=2
49 readonly LARGE_RANDOM_TRIALS
=1
51 # Global variables for stats
58 declare -a THROBBER_STR
=('-' '\\' '|' '/')
63 # Echo only if verbose option is set.
70 # Show throbber character at current cursor position.
72 info
-ne "${THROBBER_STR[$THROBBER_COUNT]}\b"
73 let THROBBER_COUNT
=$THROBBER_COUNT+1
74 let THROBBER_COUNT
=$THROBBER_COUNT%4
77 # Usage: pngout_loop <file> <png_out_options> ...
78 # Optimize the png file using pngout with the given options
79 # using various block split thresholds and filter types.
80 function pngout_loop
{
84 if [ $OPTIMIZE_LEVEL == 1 ]; then
85 for j
in $
(eval echo {0.
.5}); do
87 pngout
-q -k1 -s1 -f$j $opts $file
90 for i
in 0 128 256 512; do
91 for j
in $
(eval echo {0.
.5}); do
93 pngout
-q -k1 -s1 -b$i -f$j $opts $file
99 # Usage: get_color_depth_list
100 # Returns the list of color depth options for current optimization level.
101 function get_color_depth_list
{
102 if [ $OPTIMIZE_LEVEL == 1 ]; then
105 echo "-d1 -d2 -d4 -d8"
109 # Usage: process_grayscale <file>
110 # Optimize grayscale images for all color bit depths.
112 # TODO(oshima): Experiment with -d0 w/o -c0.
113 function process_grayscale
{
114 info
-ne "\b\b\b\b\b\b\b\bgray...."
115 for opt
in $
(get_color_depth_list
); do
116 pngout_loop
$file -c0 $opt
120 # Usage: process_grayscale_alpha <file>
121 # Optimize grayscale images with alpha for all color bit depths.
122 function process_grayscale_alpha
{
123 info
-ne "\b\b\b\b\b\b\b\bgray-a.."
124 pngout_loop
$file -c4
125 for opt
in $
(get_color_depth_list
); do
126 pngout_loop
$file -c3 $opt
130 # Usage: process_rgb <file>
131 # Optimize rgb images with or without alpha for all color bit depths.
132 function process_rgb
{
133 info
-ne "\b\b\b\b\b\b\b\brgb....."
134 for opt
in $
(get_color_depth_list
); do
135 pngout_loop
$file -c3 $opt
137 pngout_loop
$file -c2
138 pngout_loop
$file -c6
141 # Usage: huffman_blocks <file>
142 # Optimize the huffman blocks.
143 function huffman_blocks
{
144 info
-ne "\b\b\b\b\b\b\b\bhuffman."
146 local size
=$
(stat
-c%s
$file)
147 local min_block_size
=$DEFAULT_MIN_BLOCK_SIZE
148 local limit_blocks
=$DEFAULT_LIMIT_BLOCKS
150 if [ $size -gt $LARGE_FILE_THRESHOLD ]; then
151 min_block_size
=$LARGE_MIN_BLOCK_SIZE
152 limit_blocks
=$LARGE_LIMIT_BLOCKS
154 let max_blocks
=$size/$min_block_size
155 if [ $max_blocks -gt $limit_blocks ]; then
156 max_blocks
=$limit_blocks
159 for i
in $
(eval echo {2..
$max_blocks}); do
161 pngout
-q -k1 -ks -s1 -n$i $file
165 # Usage: random_huffman_table_trial <file>
166 # Try compressing by randomizing the initial huffman table.
168 # TODO(oshima): Try adjusting different parameters for large files to
170 function random_huffman_table_trial
{
171 info
-ne "\b\b\b\b\b\b\b\brandom.."
173 local old_size
=$
(stat
-c%s
$file)
174 local trials_count
=$DEFAULT_RANDOM_TRIALS
176 if [ $old_size -gt $LARGE_FILE_THRESHOLD ]; then
177 trials_count
=$LARGE_RANDOM_TRIALS
179 for i
in $
(eval echo {1..
$trials_count}); do
181 pngout
-q -k1 -ks -s0 -r $file
183 local new_size
=$
(stat
-c%s
$file)
184 if [ $new_size -lt $old_size ]; then
185 random_huffman_table_trial
$file
189 # Usage: final_comprssion <file>
190 # Further compress using optipng and advdef.
191 # TODO(oshima): Experiment with 256.
192 function final_compression
{
193 info
-ne "\b\b\b\b\b\b\b\bfinal..."
195 if [ $OPTIMIZE_LEVEL == 2 ]; then
196 for i
in 32k
16k
8k
4k
2k
1k
512; do
198 optipng
-q -nb -nc -zw$i -zc1-9 -zm1-9 -zs0-3 -f0-5 $file
201 for i
in $
(eval echo {1.
.4}); do
203 advdef
-q -z -$i $file
206 # Clear the current line.
208 printf "\033[0G\033[K"
212 # Usage: get_color_type <file>
213 # Returns the color type name of the png file. Here is the list of names
214 # for each color type codes.
220 # See http://en.wikipedia.org/wiki/Portable_Network_Graphics#Color_depth
221 # for details about the color type code.
222 function get_color_type
{
224 echo $
(file $file |
awk -F, '{print $3}' |
awk '{print $2}')
227 # Usage: optimize_size <file>
228 # Performs png file optimization.
229 function optimize_size
{
230 # Print filename, trimmed to ensure it + status don't take more than 1 line
231 local filename_length
=${#file}
232 local -i allowed_length
=$COLUMNS-11
233 local -i trimmed_length
=$filename_length-$COLUMNS+14
234 if [ "$filename_length" -lt "$allowed_length" ]; then
235 info
-n "$file|........"
237 info
-n "...${file:$trimmed_length}|........"
242 advdef
-q -z -4 $file
244 pngout
-q -s4 -c0 -force $file $file.tmp.png
245 if [ -f $file.tmp.png
]; then
247 process_grayscale
$file
248 process_grayscale_alpha
$file
250 pngout
-q -s4 -c4 -force $file $file.tmp.png
251 if [ -f $file.tmp.png
]; then
253 process_grayscale_alpha
$file
259 info
-ne "\b\b\b\b\b\b\b\bfilter.."
260 local old_color_type
=$
(get_color_type
$file)
261 optipng
-q -zc9 -zm8 -zs0-3 -f0-5 $file -out $file.tmp.png
262 local new_color_type
=$
(get_color_type
$file.tmp.png
)
263 # optipng may corrupt a png file when reducing the color type
264 # to grayscale/grayscale+alpha. Just skip such cases until
265 # the bug is fixed. See crbug.com/174505, crbug.com/174084.
266 # The issue is reported in
267 # https://sourceforge.net/tracker/?func=detail&aid=3603630&group_id=151404&atid=780913
268 if [[ $old_color_type == "RGBA" && $new_color_type == gray
* ]] ; then
271 mv $file.tmp.png
$file
273 pngout
-q -k1 -s1 $file
277 # TODO(oshima): Experiment with strategy 1.
278 info
-ne "\b\b\b\b\b\b\b\bstrategy"
279 if [ $OPTIMIZE_LEVEL == 2 ]; then
281 pngout
-q -k1 -ks -s$i $file
284 pngout
-q -k1 -ks -s1 $file
287 if [ $OPTIMIZE_LEVEL == 2 ]; then
288 random_huffman_table_trial
$file
291 final_compression
$file
294 # Usage: process_file <file>
295 function process_file
{
297 local name
=$
(basename $file)
298 # -rem alla removes all ancillary chunks except for tRNS
299 pngcrush
-d $TMP_DIR -brute -reduce -rem alla
$file > /dev
/null
2>&1
301 if [ -f $TMP_DIR/$name -a $OPTIMIZE_LEVEL != 0 ]; then
302 optimize_size
$TMP_DIR/$name
306 # Usage: optimize_file <file>
307 function optimize_file
{
309 if $using_cygwin ; then
310 file=$
(cygpath
-w $file)
313 local name
=$
(basename $file)
314 local old
=$
(stat
-c%s
$file)
315 local tmp_file
=$TMP_DIR/$name
320 if [ ! -e $tmp_file ] ; then
321 let CORRUPTED_FILE
+=1
322 echo "$file may be corrupted; skipping\n"
326 local new
=$
(stat
-c%s
$tmp_file)
328 let percent
=$diff*100
329 let percent
=$percent/$old
331 if [ $new -lt $old ]; then
332 info
"$file: $old => $new ($diff bytes: $percent%)"
333 cp "$tmp_file" "$file"
334 let TOTAL_OLD_BYTES
+=$old
335 let TOTAL_NEW_BYTES
+=$new
336 let PROCESSED_FILE
+=1
338 if [ $OPTIMIZE_LEVEL == 0 ]; then
339 info
"$file: Skipped"
341 info
"$file: Unable to reduce size"
347 function optimize_dir
{
349 if $using_cygwin ; then
350 dir
=$
(cygpath
-w $dir)
353 for f
in $
(find $dir -name "*.png"); do
358 function install_if_not_installed
{
361 which $program > /dev
/null
2>&1
362 if [ "$?" != "0" ]; then
363 if $using_cygwin ; then
364 echo "Couldn't find $program. " \
365 "Please run cygwin's setup.exe and install the $package package."
368 read -p "Couldn't find $program. Do you want to install? (y/n)"
369 [ "$REPLY" == "y" ] && sudo apt-get
install $package
370 [ "$REPLY" == "y" ] ||
exit
375 function fail_if_not_installed
{
378 which $program > /dev
/null
2>&1
380 echo "Couldn't find $program. Please download and install it from $url ."
385 # Check pngcrush version and exit if the version is in bad range.
386 # See crbug.com/404893.
387 function exit_if_bad_pngcrush_version
{
388 local version
=$
(pngcrush
-v 2>&1 |
awk "/pngcrush 1.7./ {print \$3}")
389 local version_num
=$
(echo $version |
sed "s/\.//g")
390 if [[ (1748 -lt $version_num && $version_num -lt 1773) ]] ; then
391 echo "Your pngcrush ($version) has a bug that exists from " \
392 "1.7.49 to 1.7.72 (see crbug.com/404893 for details)."
393 echo "Please upgrade pngcrush and try again"
399 local program
=$
(basename $0)
401 "Usage: $program [options] <dir> ...
403 $program is a utility to reduce the size of png files by removing
404 unnecessary chunks and compressing the image.
407 -o<optimize_level> Specify optimization level: (default is 1)
408 0 Just run pngcrush. It removes unnecessary chunks and perform basic
409 optimization on the encoded data.
410 1 Optimize png files using pngout/optipng and advdef. This can further
411 reduce addtional 5~30%. This is the default level.
412 2 Aggressively optimize the size of png files. This may produce
413 addtional 1%~5% reduction. Warning: this is *VERY*
414 slow and can take hours to process all files.
415 -r<revision> If this is specified, the script processes only png files
416 changed since this revision. The <dir> options will be used
417 to narrow down the files under specific directories.
418 -v Shows optimization process for each file.
419 -h Print this help text."
423 if [ ! -e ..
/.gclient
]; then
424 echo "$0 must be run in src directory"
428 if [ "$(expr substr $(uname -s) 1 6)" == "CYGWIN" ]; then
434 # The -i in the shebang line should result in $COLUMNS being set on newer
435 # versions of bash. If it's not set yet, attempt to set it.
436 if [ -z $COLUMNS ]; then
437 which tput > /dev
/null
2>&1
438 if [ "$?" == "0" ]; then
441 # No tput either... give up and just guess 80 columns.
449 while getopts o
:r
:h
:v opts
453 COMMIT
=$
(git svn find-rev r
$OPTARG |
tail -1) ||
exit
454 if [ -z "$COMMIT" ] ; then
455 echo "Revision $OPTARG not found"
460 if [[ "$OPTARG" != 0 && "$OPTARG" != 1 && "$OPTARG" != 2 ]] ; then
463 OPTIMIZE_LEVEL
=$OPTARG
473 # Remove options from argument list.
474 shift $
(($OPTIND -1))
476 # Make sure we have all necessary commands installed.
477 install_if_not_installed pngcrush pngcrush
478 exit_if_bad_pngcrush_version
480 if [ $OPTIMIZE_LEVEL -ge 1 ]; then
481 install_if_not_installed optipng optipng
483 if $using_cygwin ; then
484 fail_if_not_installed advdef
"http://advancemame.sourceforge.net/comp-readme.html"
486 install_if_not_installed advdef advancecomp
489 if $using_cygwin ; then
490 pngout_url
="http://www.advsys.net/ken/utils.htm"
492 pngout_url
="http://www.jonof.id.au/kenutils"
494 fail_if_not_installed pngout
$pngout_url
497 # Create tmp directory for crushed png file.
499 if $using_cygwin ; then
500 TMP_DIR
=$
(cygpath
-w $TMP_DIR)
503 # Make sure we cleanup temp dir
504 #trap "rm -rf $TMP_DIR" EXIT
506 # If no directories are specified, optimize all directories.
508 set ${DIRS:=$ALL_DIRS}
510 info
"Optimize level=$OPTIMIZE_LEVEL"
512 if [ -n "$COMMIT" ] ; then
513 ALL_FILES
=$
(git
diff --name-only $COMMIT HEAD
$DIRS |
grep "png$")
514 ALL_FILES_LIST
=( $ALL_FILES )
515 echo "Processing ${#ALL_FILES_LIST[*]} files"
516 for f
in $ALL_FILES; do
520 echo "Skipping deleted file: $f";
526 info
"Optimizing png files in $d"
529 elif [ -f $d ] ; then
532 echo "Not a file or directory: $d";
538 echo "Optimized $PROCESSED_FILE/$TOTAL_FILE files in" \
539 "$(date -d "0 + $SECONDS sec
" +%Ts)"
540 if [ $PROCESSED_FILE != 0 ]; then
541 let diff=$TOTAL_OLD_BYTES-$TOTAL_NEW_BYTES
542 let percent
=$diff*100/$TOTAL_OLD_BYTES
543 echo "Result: $TOTAL_OLD_BYTES => $TOTAL_NEW_BYTES bytes" \
544 "($diff bytes: $percent%)"
546 if [ $CORRUPTED_FILE != 0 ]; then
547 echo "Warning: corrupted files found: $CORRUPTED_FILE"
548 echo "Please contact the author of the CL that landed corrupted png files"