#!/bin/sh -
#
# gif2anim [options] image_anim.gif...
#
# This script uses the ImageMagick 'magick' program to take a gif animation
# (normally ending in '_anim.gif' and NOT just '.gif') and magicks it into a
# collection of X Pixmap files (or GIFs) as well as a "animation sequence
# file" which lists all settings needed to re-build the original GIF
# animation.
#
# The animation file uses the string "BASENAME" to represent the basenames for
# the individual frame images that it loads when re-creating the original GIF
# animation.  This allows you to specify a different set of images without
# changling any other settings.
#
# The resulting animation file can be used to re-build the animation either
# using the IM options contained (ignoring comments), or by giving the file to
# the "anim2gif" script which will also do the basename filename substitutions
# specified.
#
# OPTIONS
#      -c             coalesce animation before parsing
#      -t             Add time synchronization comment before each frame
#      -l             just list the anim file to stdout, no images
#      -v             Be verbose in animation conversions
#      -n             no images, just create the '.anim' file
#      -g             use an GIF suffix for frame images (default)
#      -x             use an XPM suffix for frame images
#      -s suffix      use this suffix for the frame images
#      -i initframe   number of the first frame image (def=1)
#      -b framename   basename for the individual frames
#      -o file.anim   output to this .anim file (- for stdout)
#
# NOTE: This script reqires both the ImageMagick 'magick' program and
# 'identify' programs to extract the images and information needed.
#
####
#
# WARNING: Input arguments are NOT tested for correctness.
# This script represents a security risk if used ONLINE.
# I accept no responsiblity for misuse. Use at own risk.
#
# Anthony Thyssen    25 April 1996    <anthony@cit.griffith.edu.au>
#
# 1 November 2005  Expanded with options for basename handling.
#
PROGNAME=`type $0 | awk '{print $3}'`  # search for executable on path
PROGDIR=`dirname $PROGNAME`            # extract directory of program
PROGNAME=`basename $PROGNAME`          # base name of program
Usage() {                              # output the script comments as docs
  echo >&2 "$PROGNAME:" "$@"
  sed >&2 -n '/^###/q; /^#/!q; s/^#//; s/^ //; 3s/^/Usage: /; 2,$ p' \
          "$PROGDIR/$PROGNAME"
  exit 10;
}

sfx="gif"           # Default output suffix of images to produce
init=1              # the count for the first frame in sequence
fmt="%03d"          # The format for frame count
frames=true         # output frame images by defult
basename=           # use this name for the individual frame images
animfile=           # no default animfile to output to.
all_gifs=           # forcefully magick any ".gif" file
coalesce=           # coalesce flag (off)
VERBOSE=            # be verbose (off)
time_sync=0         # add time sync comments (off)
awk=awk

while [ $# -gt 0 ]; do
  case "$1" in
  --help|--doc*) Usage ;;
  -c) coalesce='-coalesce' ;;  # coalesce animation (on)
  -t) time_sync=1 ;;           # add time sync comments (on)
  -l) frames=; animfile=- ;;   # List anim file to standard output, no images
  -v) VERBOSE=true ;;          # Be verbose
  -n) frames= ;;               # don't output any individual frames
  -s) sfx="$2"; shift ;;       # output frame images with this suffix
  -g) sfx="gif" ;;             # output frame images as GIF's (default)
  -x) sfx="xpm" ;;             # output frame images as XPM's
  -i) init="$2"; shift ;;      # the count for the first frame in sequence
  -b) basename="$2"; shift ;;  # use this basename for frames
  -o) animfile="$2"; shift ;;  # output .anim file here
  --) shift; break ;;    # end of user options
  -*) Usage "Unknown option \"$1\"" ;;
  *)  break ;;           # end of user options
  esac
  shift   # next option
done

if [ $# -eq 0 ]; then
  Usage "No GIF animations given"
fi

# echo without return (all systems, including old BSD systems)
if [ "X`echo -n`" = "X-n" ]; then
  echo_n() { echo ${1+"$@"}'\c'; }
else
  echo_n() { echo -n ${1+"$@"}; }
fi

# Is the command available on this machine?
cmd_found() {
  case "`type $1 2>&1`" in *'not found'*) return 1 ;; esac; return 0
}
cmd_found magick  || Usage "No IM magick command found -- ABORTING"
cmd_found magick identify || Usage "No IM magick identify command found -- ABORTING"

# output animation details to stdout
if [ "X$animfile" = "X-" ]; then
  animfile=/dev/stdout
fi

# use gawk if it is present. (mawk has a parse bug under Ubuntu)
if cmd_found gawk; then
  awk=gawk
fi

# -----------------------------------------------------

for i in "$@" ; do
  [ "$VERBOSE" ] && echo_n "magicking \"$i\" "
  if [ ! -f "$i" ]; then
    echo >&2 "Unable find file \"$i\""
    continue
  fi

  # --- find out the type ---
  name="`expr "//$i" : '.*/\([^/]*\)'`"           # remove path to file
  suffix="`expr "$name" : '.*\.\([^./]*\)$'`"     # extract last suffix
  name="`expr "$name" : '\(.*\)\.[^.]*$'`"        # remove last suffix

  case "$name.$suffix" in
   *.gif ) ;;
   * )     [ "$VERBOSE" ] && echo_n "${b}"
           echo >&2 "Skipping non-GIF file: \"$i\""
           continue ;;
  esac

  name=`echo "$name" | sed 's/_anim//'`        # remove any "_anim" part
  [ "$basename" ] && name=$basename
  anim_output=${animfile:-"$name.anim"}

  # Generate Animation file...
  date=`date +'%Y-%m-%d %R:%S'`
  magick "$i" $coalesce -verbose info:- |
    $awk ' #  Parse IM "identify" output, for almost direct use by "magick"
      BEGIN { print "#";
              print "# Animation Sequence File \"'"$i"'\"";
              print "# using a BASENAME of     \"'"$name"'\"";
              print "# Extracted on    '"$date"'";
              print "#";
              frame='"$init"'
              last_offset="+0+0";
              time_sync='"$time_sync"';
              time=0;
            }
      /^  Iterations:/    { if ( ! loop ) {
                              print "-loop " $NF;
                              loop = 1;
                          } }
      /^  Delay:/         { delay = $NF; sub("x100$", "", delay); }
      /^  Dispose:/       { dispose = $NF; }
      /^  Geometry:/      { size    = substr($NF,0,match($NF, /\+/ )-1 ); }
      /^  Page geometry:/ { canvas  = substr($NF,0,match($NF, /\+/ )-1 );
                            offset  = substr($NF,  match($NF, /\+/ ) );
                          }
      # End of Image Frame -- output collected image meta-data changes
      /^  Tainted: / {
          if ( ! canvas_set ) {
              print "-page " canvas;
              canvas_set = 1;
          }
          if ( time_sync )  printf "# TIME SYNC %d\n", time;
          if ( delay != last_delay ) {
              print "-delay " delay;
              last_delay = delay;
          }
          time+=delay;
          delay=0;   # no delay is given if it remains set to zero
          if ( dispose != last_dispose ) {
              print "-dispose " dispose;
              last_dispose = dispose;
          }
          if ( offset != last_offset ) {
              printf "%-16s ", "-page " offset;
              last_offset = offset;
          } else {
              printf "%-16s ", "";
          }
          printf "BASENAME_'"$fmt.$sfx"'  # %s%s\n",
                  frame++, size, offset;
        }
      END { if ( time_sync ) printf "# LOOP TIME %d\n", time; }
    ' > $anim_output
  [ "$VERBOSE" -a -f "$anim_output" ] &&
         echo_n "`grep -c \\.$sfx\$ ${name}.anim | tr -d ' '` frames"

  # split up GIF animation (reseting animation meta-data)
  if [ "$frames" ]; then
    magick "$i" $coalesce \
            +repage -set delay 0 -set dispose none -loop 0 \
            -scene $init +adjoin  "${name}_${fmt}.${sfx}"

    # Fix the X Pixmap color tables
    # using the AIcons Library "xpm-fix" script
    if [ "$sfx" = 'xpm' ] && cmd_found xpm-fix; then
      [ "$VERBOSE" ] && echo_n " xpm-fixes"
      xpm-fix `sed -n "s/ *BASENAME_/${name}_/p" ${name}.anim`
    fi
  fi

  [ "$VERBOSE" ] && echo " DONE"

done


