#!/bin/bash # # Script to strip ICC profiles from all png images and to recompress them # afterwards via optipng and advdef # # Run-time requirements: ImageMagick, OptiPNG, Advdef (part of AdvanceCOMP) # # Copyright (C) 2004 by Crossbow/Miyo # Copyright (C) 2005 by Isaac Clerencia # * (Ruby-ification and some features) # Copyright (C) 2008 by Iris Morelle # and Fabio Pedretti # * (De-ruby-ification and some other features) # * (Added advdef support) # * (Don't use .stripped files anymore) # * (Improved final statistics) # Copyright (C) 2009 by Nils Kneuper # * (Added support for parallization) # # Part of the Battle for Wesnoth Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or, # at your option any later version. This program is distributed in the # hope that it will be useful, but WITHOUT ANY WARRANTY. See the COPYING # file for more details. # ####### VARIABLES ####### current_file="" # Default optimization process nice opti_nice=19 max_number_threads=1 total_original_size=0 total_savings_size=0 total_savings_filecount=0 new_file_list="new_file_list.tmp" old_file_list="old_file_list.tmp" broken_file_list="broken_file_list.tmp" stat_args="-c %s" if [ -e "/proc/cpuinfo" ] && [ `grep processor /proc/cpuinfo | wc -l` -gt 1 ]; then max_number_threads=`grep processor /proc/cpuinfo | wc -l` fi if [ `man stat | grep 'BSD' | wc -l` -gt 0 ]; then stat_args="-f %z" fi ####### PROCEDURES ####### report_absent_tool() { echo "$1 is not present in PATH. $(basename ${0}) requires it in order to work properly." if [ -n "$2" ]; then echo "You can obtain $1 at <${2}>." fi exit -1 } user_int() { if [ -n "${current_file}" ]; then #echo "Removing temp file ${current_file}.new..." rm -f ${current_file}.new fi echo echo "Stopped by user request." echo print_statistics exit } # this part will *only* compress and not remove the temp files # those have to be removed by calling get_statistics_and_delete_temp_files optimize_imgfile() { current_file=${1} if ! [ -e ${1} ]; then echo "ERROR: image file ${1} not found" return -1 fi echo "* processing ${1} (nice = ${2})..." nice -n $2 convert -strip -define png:color-type=6 ${1} ${1}.new nice -n $2 optipng -q -o5 -nb -nc -np ${1}.new # Sometimes lower compression level gives a better compression nice -n $2 advdef -z -4 ${1}.new > /dev/null nice -n $2 advdef -z -3 ${1}.new > /dev/null nice -n $2 advdef -z -2 ${1}.new > /dev/null nice -n $2 advdef -z -1 ${1}.new > /dev/null echo ${1}.new >> ${new_file_list} echo ${1} >> ${old_file_list} } # gather statistics and remove the temp files get_statistics_and_delete_temp_files() { current_file=${1} if ! [ -e ${1} ]; then echo "ERROR: image file ${1} not found" return -1 fi echo "* processing ${1} (nice = ${2})..." if [ ! -z `cat ${broken_file_list} | cut -f1 -d " " | grep ${1}` ]; then echo "ERROR: temporary file ${1}.new has pixel differences compared to the original, not using this optimized file." rm -f ${1}.new elif [ -e ${1} ] && [ -e ${1}.new ]; then # Compare output size with original and print per-file statistics local old_png_size=`stat ${stat_args} ${1} 2> /dev/null` local new_png_size=`stat ${stat_args} ${1}.new 2> /dev/null` if [ "$old_png_size" -gt "$new_png_size" ]; then local png_savings_size=$((${old_png_size}-${new_png_size})) total_original_size=$((${total_original_size}+${old_png_size})) total_savings_size=$((${total_savings_size}+${png_savings_size})) total_savings_filecount=$((1+${total_savings_filecount})) mv -f ${1}.new ${1} echo " saved: ${png_savings_size} bytes, total saved: $((${total_savings_size}/1024)) KiB" else rm -f ${1}.new #no need to mention it when nothing actually changed... #echo " already optimized" fi else echo "ERROR: temporary file ${1}.new disappeared mysteriously" fi } print_statistics() { # Print overall statistics if [ "$total_savings_filecount" -gt 0 ]; then echo "Overall statistics (only for files with a smaller recompressed size):" echo " Original size: $((${total_original_size}/1024)) KiB on ${total_savings_filecount} files" echo " Optimized size: $(((${total_original_size}-${total_savings_size})/1024)) KiB" echo " Total saving: $((${total_savings_size}/1024)) KiB = $((${total_savings_size}*100/${total_original_size}))% decrease" else echo "No files were recompressed." fi } ####### ACTUAL SCRIPT ####### # Parse command line parameters while [ "${1}" != "" ]; do if [ "${1}" = "--nice" ] || [ "${1}" = "-n" ]; then opti_nice=$2 shift 2 elif [ "${1}" = "--threads" ]; then max_number_threads=$2 shift 2 elif [ "${1}" = "--help" ] || [ "${1}" = "-h" ]; then cat << EOSTREAM Wesnoth automatic PNG optimization helper script Part of the Battle for Wesnoth project Usage: $(basename ${0}) [{--nice|n} ] [{--threads} ] [{--help|-h}] [...] If no files are given on command line, all PNG files within current working directory and below are processed. Switches: --help / -h Displays this help text. --nice / -n Sets the process nice under which the optimization shall be done. If not specified, 19 (the lowest priority) is assumed by default. --threads Run script with worker threads. If not specified, the amount of CPUs detected will be used. This tool requires OptiPNG (http://optipng.sourceforge.net/), Advdef (http://advancemame.sourceforge.net/comp-readme.html) and ImageMagick's convert utility (http://www.imagemagick.org/script/convert.php) to be installed and available in PATH. EOSTREAM exit 0 else filelist="${filelist} ${1}" shift fi done # FIXME: disabled because it isn't working for Ivanovic. # Probe optipng which optipng &> /dev/null || report_absent_tool optipng 'http://optipng.sourceforge.net/' # Probe IM's convert which convert &>/dev/null || report_absent_tool convert 'http://www.imagemagick.org' # Probe advdef which advdef &> /dev/null || report_absent_tool advdef 'http://advancemame.sourceforge.net/comp-readme.html' # Set-up a trap to avoid leaving orphan tempfiles behind. trap user_int HUP INT TERM if test -z "$filelist"; then filelist=$(find -iname "*.png") fi # work with a parallized joblist echo "*************************" echo "* compressing the files *" echo "*************************" for f in $filelist do while [ $(jobs -pr | wc -l) -ge $max_number_threads ] do # max number of threads reached, wait for a quarter second... sleep 0.25 done #when here, we can do our normal optimization run optimize_imgfile $f $opti_nice & done # wait till all threads are done before going on in the script wait # compare compressed images pixel-by-pixel to detect possible corruption echo -ne "\n" echo "****************************************************" echo "* comparing original and new images pixel-by-pixel *" echo "****************************************************" $(dirname ${0})/compare_images.py ${old_file_list} ${new_file_list} > ${broken_file_list} if [ $? -ne 0 ]; then echo "****************************************************" echo "* Unable to compare compressed and original images *" echo "****************************************************" else echo "list of broken files saved to: ${broken_file_list}" fi # collect statistics and remove temp files # this is in a seperate part because the stats would not be correct when # run with several threads echo -ne "\n" echo "********************************************" echo "* creating statistics and moving files now *" echo "********************************************" for f in $filelist do get_statistics_and_delete_temp_files $f $opti_nice done rm -f ${old_file_list} rm -f ${new_file_list} rm -f ${broken_file_list} print_statistics exit 0