#!/bin/bash
#
# Developed by Fred Weinhaus 12/26/2007 .......... revised 11/11/2023
#
# ------------------------------------------------------------------------------
# 
# Licensing:
# 
# Copyright © Fred Weinhaus
# 
# My scripts are available free of charge for non-commercial use, ONLY.
# 
# For use of my scripts in commercial (for-profit) environments or 
# non-free applications, please contact me (Fred Weinhaus) for 
# licensing arrangements. My email address is fmw at alink dot net.
# 
# If you: 1) redistribute, 2) incorporate any of these scripts into other 
# free applications or 3) reprogram them in another scripting language, 
# then you must contact me for permission, especially if the result might 
# be used in a commercial or for-profit environment.
# 
# My scripts are also subject, in a subordinate manner, to the ImageMagick 
# license, which can be found at: http://www.imagemagick.org/script/license.php
# 
# ------------------------------------------------------------------------------
# 
####
#
# USAGE: splitimage [-m mode] [-b begin] [-f final] [-i increment] [-d delay] [ -r resize] [-v] infile1 infile2 [outfile]
# USAGE: splitimage [-h or -help]
#
# OPTIONS:
#
# -m     mode         split mode; LR, LL, RR, TB, TT or BB; default=LR
# -b     begin        (beginning) offset split position;
#                     0 to width-2 or height-2 depending upon mode;
#                     default is the center, if outfile is a single frame;
#                     default=0, if animation with or without outfile
# -f     final        (final) offset split position for animation;
#                     1 to width-1 or height-1 depending upon mode;
#                     default=width-1 or height-1, depending upon mode
# -i     increment    incremental shift of one image relative to second
#                     per animation frame; 0 to width/4 or height/4; 
#                     default=10 pixels if no outfile provided;
#                     default=0 with outfile indicates single frame output         
# -d     delay        time delay per animation frame (ms); delay >= 0; default=3
# -r     resize       image resize percentage; resize > 0; 
#                     default=100 (unchanged)
# -v                  indicates to view (display) the outfile automatically
# outfile             if no oufile is provided, an animation will be generated 
#                     and viewed (displayed) automatically without specifying
#                     the -v option
#
###
#
# NAME: SPLITIMAGE 
# 
# PURPOSE: To generate a side-by-side append of two partial images with an 
# option to animate the amount of each image used in the append. 
# 
# DESCRIPTION: SPLITIMAGE generates a side-by-side append of two partial images, nominally
# half of each with the left half of the first image and right half of the second image. 
# However, one can specify to use two left halves or two right halves or the top and 
# bottom or two top halves or two bottom halves. Optionally the split between the two 
# images can be animated for visual comparison of the difference/change between the 
# the two images and the result saved and/or automatically displayed.
# 
# 
# OPTIONS: 
# 
# 
# -m mode ... MODE is the split orientation of the two images. The choices are:
# LR (left/right), LL (left/left), RR (right/right), TB (top/bottom), TT (top/top),
# or BB (bottom/bottom)
# 
# -b begin ... BEGIN is the position for the split for a single frame output or the 
# beginning position for an animation. The values may be between 0 and width-2 or 
# height-2. The default is the center at width/2 or height/2 depending upon mode.
# 
# -f final ... FINAL is the final position for the split for an animation before  
# it reverses and goes back to the beginning position to form a loop. The values 
# may be between 1 and width-1 or height-1, but must be greater than Begin. 
# The default is the width-1 or height-1 depending upon mode. Final is ignored 
# if there is no animation.
# 
# -i increment ... INCREMENT is the pixel offset amount for the split location
# for each new frame of the animation. Increment values should be integers between 
# 0 to width/4 or height/4, depending upon the mode. The default=0 indicates no 
# animation and that a single frame output will be generated. If no outfile is 
# specified, then an animation will automatically be generated and displayed. 
# The default value in this case is 10 pixels.
# 
# -d delay ... DELAY is the time delay in msec between frames in the animation. 
# Values are integers greater than or equal to zero. The default=3. Note that 
# the animation will be created to loop forever. Delay is ignored if a single 
# frame output is to be generated by specifying increment=0. The 
# delay=3 will make a fast animation if displayed under X11. However, if the 
# animation is saved to file and displayed in a browser, the browser will likely 
# impose a larger delay per frame and the animation will appear slower.
# 
# -r resize ... RESIZE allows the output to made larger or smaller than 
# the input image size. The values are integers greater than 0 representing the 
# resize percentage. The default=100 which leaves the output the same size as 
# the input image.
# 
# -v ... Specifies that along with the output animation, the outfile is to be 
# viewed (displayed) automatically before the script is ended. If no output 
# animation file is provided, the animation will be created and viewed 
# automatically even if the -v option is not specified. This parameter is 
# ignored if the output is a single frame image (increment=0).
# 
# infile1 and infile2 ... Both input images must be the same size.
# 
# outfile ... OUTFILE, if provided for an animation, must be in a format such as 
# gif that supports multiframe animations.
# 
# CAVEAT: No guarantee that this script will work on all platforms, 
# nor that trapping of inconsistent parameters is complete and 
# foolproof. Use At Your Own Risk. 
# 
######
#
# set default values
mode="LR"		# output/animation mode
begin=""    	# begin animation location
final=""		# final animation location
incr=0			# animation increment
delay=3     	# delay between animation frames
resize=''   	# output resize
view="no"   	# automatic view (display)

# set directory for temporary files
dir="."    # suggestions are dir="." or dir="/tmp"

# set up functions to report Usage and Usage with Description
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
usage1() 
	{
	echo >&2 ""
	echo >&2 "$PROGNAME:" "$@"
	sed >&2 -e '1,/^####/d;  /^###/g;  /^#/!q;  s/^#//;  s/^ //;  4,$p' "$PROGDIR/$PROGNAME"
	}
usage2() 
	{
	echo >&2 ""
	echo >&2 "$PROGNAME:" "$@"
	sed >&2 -e '1,/^####/d;  /^######/g;  /^#/!q;  s/^#*//;  s/^ //;  4,$p' "$PROGDIR/$PROGNAME"
	}

# function to report error messages
errMsg()
	{
	echo ""
	echo $1
	echo ""
	usage1
	exit 1
	}

# function to test for minus at start of value of second part of option 1 or 2
checkMinus()
	{
	test=`echo "$1" | grep -c '^-.*$'`   # returns 1 if match; 0 otherwise
    [ $test -eq 1 ] && errMsg "$errorMsg"
	}

# test for correct number of arguments and get values
if [ $# -eq 0 ]
	then
	# help information
   echo ""
   usage2
   exit 0
elif [ $# -gt 16 ]
	then
	errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---"
else
	while [ $# -gt 0 ]
		do
			# get parameter values
			case "$1" in
		  -h|-help)    # help information
					   echo ""
					   usage2
					   exit 0
					   ;;
				-m)    # mode
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID MODE SPECIFICATION ---"
					   checkMinus "$1"
					   mode="$1"
					   # further testing done below
					   ;;
				-b)    # beginning position
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID BEGIN SPECIFICATION ---"
					   checkMinus "$1"
					   begin="$1"
					   begintest=`expr "$begin" : '^[0-9\][0-9]*$'`
		   			   [ $begintest -eq 0 ] && errMsg "--- BEGIN=$begin MUST BE A NON-NEGATIVE INTEGER ---"
					   ;;
				-f)    # final position
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID FINAL SPECIFICATION ---"
					   checkMinus "$1"
					   final="$1"
					   finaltest=`expr "$final" : '^[0-9\][0-9]*$'`
		   			   [ $finaltest -eq 0 ] && errMsg "--- FINAL=$final MUST BE A NON-NEGATIVE INTEGER ---"
					   ;;
				-i)    # animation pixel increment
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID INCREMENT SPECIFICATION ---"
					   checkMinus "$1"
					   incr="$1"
					   incrtest=`expr "$incr" : '^[0-9\][0-9]*$'`
		   			   [ $incrtest -eq 0 ] && errMsg "--- INCREMENT=$incr MUST BE A NON-NEGATIVE INTEGER ---"
					   ;;
				-d)    # delay
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID DELAY SPECIFICATION ---"
					   checkMinus "$1"
					   delay="$1"
					   delaytest=`expr "$delay" : '^[0-9][0-9]*$'`
		   			   [ $delaytest -eq 0 ] && errMsg "--- DELAY=$delay MUST BE A NON-NEGATIVE INTEGER ---"
					   ;;
				-r)    # resize
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID RESIZE SPECIFICATION ---"
					   checkMinus "$1"
					   resize="$1"
					   resizetest=`expr "$resize" : '^[0-9][0-9]*$'`
		   			   resizetestA=`echo "$resize < 1" | bc`
		   			   [ $resizetest -eq 0 ] && errMsg "--- RESIZE=$resize MUST BE AN INTEGER ---"
					   [ $resizetestA -eq 1 ] && errMsg "--- RESIZE=$resize MUST BE GREATER THAN 0 ---"
					   resize="-resize $1%"
					   ;;
				-v)    # view
					   view="yes"
					   ;;
				 -)    # STDIN and end of arguments
					   break
					   ;;
				-*)    # any other - argument
					   errMsg "--- UNKNOWN OPTION ---"
					   ;;
				 *)    # end of arguments
					   break
					   ;;
			esac
			shift   # next option
	done
	#
	# get infile and outfile
	infile1="$1"
	infile2="$2"
	outfile="$3"
fi

: '
echo "infile1=$infile1"
echo "infile2=$infile2"
echo "outfile=$outfile"
'

# test that infile1 and infile2 provided
[ "$infile1" = "" ] && errMsg "NO INPUT FILE 1 SPECIFIED"
[ "$infile2" = "" ] && errMsg "NO INPUT FILE 2 SPECIFIED"

# setup temporary images and auto delete upon exit
# use mpc/cache to hold input image temporarily in memory
tmp1A="$dir/split_1_$$.mpc"
tmp1B="$dir/split_1_$$.cache"
tmp2A="$dir/split_2_$$.mpc"
tmp2B="$dir/split_2_$$.cache"
tmp0="$dir/split_0_$$.miff"
trap "rm -f $tmp1A $tmp1B $tmp2A $tmp2B $tmp0;" 0
trap "rm -f $tmp1A $tmp1B $tmp2A $tmp2B $tmp0; exit 1" 1 2 3 15
#trap "rm -f $tmp1A $tmp1B $tmp2A $tmp2B $tmp0; exit 1" ERR


# get im_version
im_version=`convert -list configure | \
	sed '/^LIB_VERSION_NUMBER */!d; s//,/;  s/,/,0/g;  s/,0*\([0-9][0-9]\)/\1/g' | head -n 1`

if [ "$im_version" -ge "07000000" ]; then
	identifying="magick identify"
else
	identifying="identify"
fi

if convert -quiet "$infile1" $resize +repage "$tmp1A"
	then
	width1=`$identifying -format %w $tmp1A`
	height1=`$identifying -format %h $tmp1A`
else
	errMsg "--- FILE $infile1 DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---"
fi

if convert -quiet -regard-warnings "$infile2" $resize +repage "$tmp2A"
	then
	width2=`$identifying -format %w $tmp2A`
	height2=`$identifying -format %h $tmp2A`
else
	errMsg "--- FILE $infile2 DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---"
fi

[ $width1 -ne $width2 ] && errMsg "--- INPUT IMAGE WIDTHS DO NOT MATCH ---"
[ $height1 -ne $height2 ] && errMsg "--- INPUT IMAGE WIDTHS DO NOT MATCH ---"

width=$width1
height=$height1


if [ "$mode" = "LR" -o "$mode" = "LL" -o "$mode" = "LR" -o "$mode" = "lr" -o "$mode" = "ll" -o "$mode" = "rr" ]
	then
	testincr=`echo "$incr > ($width / 4)" | bc`
	[ $testincr -eq 1 ] && errMsg "--- INCREMENT MUST BE LESS THAN OR EQUAL WIDTH/4 ---"
	if [ "$begin" != "" ]
		then
		testbeginA=`echo "$begin < 0" | bc`
		testbeginB=`echo "$begin > $width - 2" | bc`
		[ $testbeginA -eq 1 -o $testbeginB -eq 1 ] && errMsg "--- BEGIN MUST BE BETWEEN 0 AND WIDTH-2 ---"
	fi
	if [ "$final" != "" ]
		then
		testfinalA=`echo "$final < 1" | bc`
		testfinalB=`echo "$final > $width - 1" | bc`
		[ $testfinalA -eq 1 -o $testfinalB -eq 1 ] && errMsg "--- FINAL MUST BE BETWEEN 1 AND WIDTH-1 ---"
	fi
else
	testincr=`echo "$incr > ($height / 4)" | bc`
	[ $testincr -eq 1 ] && errMsg "--- INCREMENT MUST BE LESS THAN OR EQUAL HEIGHT/4 ---"
	if [ "$begin" != "" ]
		then
		testbeginA=`echo "$begin < 0" | bc`
		testbeginB=`echo "$begin > $height - 2" | bc`
		[ $testbeginA -eq 1 -o $testbeginB -eq 1 ] && errMsg "--- BEGIN MUST BE BETWEEN 0 AND HEIGHT-2 ---"
	fi
	if [ "$final" != "" ]
		then
		testfinalA=`echo "$final < 1" | bc`
		testfinalB=`echo "$final > $height - 1" | bc`
		[ $testfinalA -eq 1 -o $testfinalB -eq 1 ] && errMsg "--- FINAL MUST BE BETWEEN 1 AND HEIGHT-1 ---"
	fi
fi


# set default values

: '
echo "incr=$incr"
echo "delay=$delay"
'

if [ $incr -eq 0 -a "$outfile" != "" ]
	then
	# single frame output
	if [ "$mode" = "LR" -o "$mode" = "LL" -o "$mode" = "RR" -o "$mode" = "lr" -o "$mode" = "ll" -o "$mode" = "rr" ]
		then
		begin=`echo "scale=0; ((($width + 1) / 2) - 1)/ 1" | bc`
	else
		begin=`echo "scale=0; ((($height + 1) / 2) - 1)/ 1" | bc`
	fi
else
	# animation
	[ "$begin" = "" ] && begin=0
	[ $incr = 0 ] && incr=10
	[ $delay = 0 ] && delay=3
	if [ "$mode" = "LR" -o "$mode" = "LL" -o "$mode" = "RR" -o "$mode" = "lr" -o "$mode" = "ll" -o "$mode" = "rr" ]
		then
		[ "$final" = "" ] && final=`expr $width - 1`
	else
		[ "$final" = "" ] && final=`expr $height - 1`
	fi
fi

: '
echo "begin=$begin"
echo "final=$final"
echo "incr=$incr"
echo "delay=$delay"
echo "view=$view"
'

# set negative increment
incrm=`expr 0 - $incr`

# first half animation function
animateFrames()
	{
	incrw1=$1
	incrh1=$2
	incrx1=$3
	incry1=$4
	incrw2=$5
	incrh2=$6
	incrx2=$7
	incry2=$8
	append=$9
	i=1
	echo $i
	while [ $i -lt $numframes ]
		do
		i=`expr $i + 1`
		echo $i
		wd1=`expr $wd1 + $incrw1`
		ht1=`expr $ht1 + $incrh1`
		ox1=`expr $ox1 + $incrx1`
		oy1=`expr $oy1 + $incry1`
		wd2=`expr $wd2 + $incrw2`
		ht2=`expr $ht2 + $incrh2`
		ox2=`expr $ox2 + $incrx2`
		oy2=`expr $oy2 + $incry2`
		
: '
echo "wd1=$wd1"
echo "ht1=$ht1"
echo "ox1=$ox1"
echo "oy1=$oy1"
echo "wd2=$wd2"
echo "ht2=$ht2"
echo "ox2=$ox2"
echo "oy2=$oy2"
'

		convert $tmp1A[${wd1}x${ht1}+${ox1}+${oy1}] $tmp2A[${wd2}x${ht2}+${ox2}+${oy2}] \
			$append miff:- | convert -delay $delay $tmp0 - +repage $tmp0
	done
	}

# create first or single frame image
case "$mode" in
	LR|lr) 	wd1=`expr $begin + 1`
			ht1=$height
			ox1=0
			oy1=0
			wd2=`expr $width - $begin - 1`
			ht2=$height
			ox2=`expr $begin + 1`
			oy2=0
			append="+append"
			;;
	LL|ll) 	wd1=`expr $begin + 1`
			ht1=$height
			ox1=0
			oy1=0
			wd2=`expr $width - $begin - 1`
			ht2=$height
			ox2=0
			oy2=0
			append="+append"
			;;
	RR|rr) 	wd1=`expr $begin + 1`
			ht1=$height
			ox1=`expr $width - $begin - 1`
			oy1=0
			wd2=`expr $width - $begin - 1`
			ht2=$height
			ox2=`expr $begin + 1`
			oy2=0
			append="+append"
			;;
	
	TB|tb) 	wd1=$width
			ht1=`expr $begin + 1`
			ox1=0
			oy1=0
			wd2=$width
			ht2=`expr $height - $begin - 1`
			ox2=0
			oy2=`expr $begin + 1`
			append="-append"
			;;
	TT|tt) 	wd1=$width
			ht1=`expr $begin + 1`
			ox1=0
			oy1=0
			wd2=$width
			ht2=`expr $height - $begin - 1`
			ox2=0
			oy2=0
			append="-append"
			;;
	BB|bb) 	wd1=$width
			ht1=`expr $begin + 1`
			ox1=0
			oy1=`expr $height - $begin - 1`
			wd2=$width
			ht2=`expr $height - $begin - 1`
			ox2=0
			oy2=`expr $begin + 1`
			append="-append"
			;;
	  *)    # any other - argument
		    errMsg "--- UNKNOWN MODE ---"
		    ;;
esac

: '
echo "wd1=$wd1"
echo "ht1=$ht1"
echo "ox1=$ox1"
echo "oy1=$oy1"
echo "wd2=$wd2"
echo "ht2=$ht2"
echo "ox2=$ox2"
echo "oy2=$oy2"
'

convert $tmp1A[${wd1}x${ht1}+${ox1}+${oy1}] $tmp2A[${wd2}x${ht2}+${ox2}+${oy2}] $append +repage $tmp0

if [ $incr -eq 0 -a "$outfile" != "" ]
	then
	#single frame output
	convert $tmp0 "$outfile"

	# display outfile
	if [ "$view" = "yes" ]
		then
		display $tmp0
	fi

else
	# create first half of animation
	numframes=`echo "scale=0; ((($final - $begin) / $incr) + 1)/ 1" | bc`
	echo ""
	echo "Please Wait - $numframes Frames Being Created"
	echo ""
	case "$mode" in
		LR|lr)	animateFrames $incr 0 0 0 $incrm 0 $incr 0 "+append"
				;;
		LL|ll) 	animateFrames $incr 0 0 0 $incrm 0 0 0 "+append"
				;;
		RR|rr) 	animateFrames $incr 0 $incrm 0 $incrm 0 $incr 0 "+append"
				;;
		
		TB|tb) 	animateFrames 0 $incr 0 0 0 $incrm 0 $incr "-append"
				;;
		TT|tt) 	animateFrames 0 $incr 0 0 0 $incrm 0 0 "-append"
				;;
		BB|bb) 	animateFrames 0 $incr 0 $incrm 0 $incrm 0 $incr "-append"
				;;
		    *)  # any other - argument
				errMsg "--- UNKNOWN MODE ---"
				;;
	esac
	
	# create second half of animation and merge with first half
	convert $tmp0 -coalesce \( -clone -2-1 \) -loop 0 $tmp0
	
	# save output animation
	if [ "$outfile" != "" ]
		then
		echo ""
		echo "Saving Output Animation"
		echo ""
		convert $tmp0 "$outfile"
	fi
	
	# display animation
	if [ "$outfile" = "" -o "$view" = "yes" ]
		then
		animate $tmp0
	fi
fi
exit 0