#! /bin/bash
# vim: set filetype=sh:

# opt-jpg: Recompresses .jpg files

# Copyright (C) 2004-2020 by Brian Lindholm.  This file is part of the
# littleutils utility set.
#
# The opt-jpg utility is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later version.
#
# The opt-jpg utility is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# the littleutils.  If not, see <https://www.gnu.org/licenses/>.

# get a valid temporary directory and set up traps
TMPWILD=`tempname -w opt-jpg_$$` || exit 99
trap 'rm -f ${TMPWILD} ; exit 1' 1 2 3 13 15
trap 'rm -f ${TMPWILD} ; exit 0' 0

# get command-line options
ARITHMETIC=n
GRAY=n
MARKERS=none
TOUCH=n
VERBOSE=y
while getopts aghm:qt opts
do
  case $opts in
    a) ARITHMETIC=y ;;
    g) GRAY=y ;;
    h) echo 'opt-jpg 1.2.3'
       echo 'usage: opt-jpg [-a(rithmetic)] [-g(ray)] [-h(elp)] [-m MARKERS]'
       echo '         [-q(uiet)] [-t(ouch)] JPEG_filename ...'
       exit 0 ;;
    m) MARKERS=${OPTARG} ;;
    q) VERBOSE=n ;;
    t) TOUCH=y ;;
    *) echo 'opt-jpg 1.2.3'
       echo 'usage: opt-jpg [-a(rithmetic)] [-g(ray)] [-h(elp)] [-m MARKERS]'
       echo '         [-q(uiet)] [-t(ouch)] JPEG_filename ...'
       exit 1 ;;
  esac
done
shift `expr ${OPTIND} - 1`

# run through files
while [ $# -gt 0 ]; do

  # make sure we can read and modify file
  if [ ! -f "$1" -o ! -r "$1" -o ! -w "$1" ]; then
    echo "opt-jpg warning: $1 is not a writeable non-directory file"
    shift; continue
  fi

  # are we forcing to grayscale?
  if [ "$GRAY" = 'y' ]; then
    imagsize "$1" | grep -v -q 'depth=8'
    OUT0="$?"
    if [ "$OUT0" -eq 1 ]; then
      FORCE=n
    else
      FORCE=y
    fi
  else
    FORCE=N
  fi

  # trial 1: optimally recompress
  TMPJPGX=`tempname -s .jpg opt-jpg_$$_1` || exit 99
  if [ "$FORCE" = 'y' ]; then
    jpegtran -grayscale -optimize -copy ${MARKERS} -outfile ${TMPJPGX} "$1"
  else
    jpegtran -optimize -copy ${MARKERS} -outfile ${TMPJPGX} "$1"
  fi
  OUT1="$?"
  if [ "$OUT1" -ne 0 -a "$OUT1" -ne 2 ]; then
    echo "opt-jpg error: $1 is a bad jpg file: jpegtran rc = ${OUT1}"
    rm -f ${TMPJPGX}
    shift; continue
  fi
  SX=`filesize "$TMPJPGX"`

  # trial 2: optimally recompress with progressive scan
  TMPJPG2=`tempname -s .jpg opt-jpg_$$_2` || exit 99
  if [ "$FORCE" = 'y' ]; then
    jpegtran -grayscale -progressive -optimize -copy ${MARKERS} -outfile ${TMPJPG2} "$1"
  else
    jpegtran -progressive -optimize -copy ${MARKERS} -outfile ${TMPJPG2} "$1"
  fi
  OUT2="$?"
  if [ "$OUT2" -ne 0 -a "$OUT2" -ne 2 ]; then
    echo "opt-jpg error: $1 is a bad jpg file: jpegtran rc = ${OUT2}"
    rm -f ${TMPJPGX} ${TMPJPG2}
    shift; continue
  fi
  # overwrite previous trial if smaller
  S2=`filesize "$TMPJPG2"`
  if [ "$S2" -lt "$SX" ]; then
    mv ${TMPJPG2} ${TMPJPGX}
    SX=${S2}
  else
    rm -f ${TMPJPG2}
  fi

  # trial 3: optimally recompress with arithmetic encoding
  if [ "$ARITHMETIC" = 'y' ]; then
    TMPJPG3=`tempname -s .jpg opt-jpg_$$_3` || exit 99
    if [ "$FORCE" = 'y' ]; then
      jpegtran -grayscale -arithmetic -copy ${MARKERS} -outfile ${TMPJPG3} "$1"
    else
      jpegtran -arithmetic -copy ${MARKERS} -outfile ${TMPJPG3} "$1"
    fi
    OUT3="$?"
    if [ "$OUT3" -ne 0 -a "$OUT3" -ne 2 ]; then
      echo "opt-jpg error: $1 is a bad jpg file: jpegtran rc = ${OUT3}"
      rm -f ${TMPJPGX} ${TMPJPG2} ${TMPJPG3}
      shift; continue
    fi
    # overwrite previous trial if smaller
    S3=`filesize "$TMPJPG3"`
    if [ "$S3" -lt "$SX" ]; then
      mv ${TMPJPG3} ${TMPJPGX}
      SX=${S3}
    else
      rm -f ${TMPJPG3}
    fi
  fi

  # trial 4: optimally recompress with progressive scan and arithmetic encoding
  if [ "$ARITHMETIC" = 'y' ]; then
    TMPJPG4=`tempname -s .jpg opt-jpg_$$_4` || exit 99
    if [ "$FORCE" = 'y' ]; then
      jpegtran -grayscale -progressive -arithmetic -copy ${MARKERS} -outfile ${TMPJPG4} "$1"
    else
      jpegtran -progressive -arithmetic -copy ${MARKERS} -outfile ${TMPJPG4} "$1"
    fi
    OUT4="$?"
    if [ "$OUT4" -ne 0 -a "$OUT4" -ne 2 ]; then
      echo "opt-jpg error: $1 is a bad jpg file: jpegtran rc = ${OUT4}"
      rm -f ${TMPJPGX} ${TMPJPG2} ${TMPJPG3} ${TMPJPG4}
      shift; continue
    fi
    # overwrite previous trial if smaller
    S4=`filesize "$TMPJPG4"`
    if [ "$S4" -lt "$SX" ]; then
      mv ${TMPJPG4} ${TMPJPGX}
      SX=${S4}
    else
      rm -f ${TMPJPG4}
    fi
  fi

  # ensure that smallest trial is smaller than original file
  if [ "$TOUCH" = 'y' ]; then
    touch -r "$1" ${TMPJPGX}
  fi
  S0=`filesize "$1"`
  if [ "$FORCE" = 'y' -o "$SX" -lt "$S0" ]; then
    cp --preserve=timestamps ${TMPJPGX} "$1"
    if [ "$VERBOSE" = 'y' ]; then
      echo "$1: ${S0} vs. ${SX}"
    fi
  else
    if [ "$VERBOSE" = 'y' ]; then
      echo "$1: unchanged"
    fi
  fi

  # clean up afterwards
  rm -f ${TMPJPGX}
  shift

done
rm -f ${TMPWILD}
