#!/bin/bash
#
# Developed by Fred Weinhaus 1/20/2012 .......... 11/22/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: vignette2 [-a amount] [-d dim] [-s shape] [-r round] [-c color] 
#        [-m method] [-i] infile outfile
#
# USAGE: vignette2 [-help|-h]
#
# OPTIONS:
#
# -a     amount     vignette amount; blur sigma value; integer>=0; default=25
# -d     dim        vignette region boundary; relative size compared to the
#                   input image expressed as width,height in percent or pixels;
#                   integer>0; if percent, add % symbol; default=90%
# -s     shape      shape of vignette region boundary; choices are: 
#                   rectangle, roundrectangle, ellipse or circle; 
#                   default=roundrectangle
# -r     round      amount of rounding for roundrectangle; integer>0; 
#                   default=2*amount
# -c     color      vignette color; any opaque IM color allowed or "none" 
#                   for a transparency vignette; default=black
# -m     method     vignette color compose method; any IM compose method; 
#                   default=multiply
# -i                invert the vignette effect from outside to inside
#  
#
###
#
# NAME: VIGNETTE2 
# 
# PURPOSE: Applies a vignette effect to a picture.
# 
# DESCRIPTION: VIGNETTER2 applies a vignette effect to a picture. The vignette 
# shape may be any of the following: rectangle, roundrectangle, ellipse or 
# circle. This is similar to the way Photoshop does vignetting.
# 
# 
# OPTIONS: 
# 
# -a amount ... AMOUNT is the vignette amount expressed as a blur sigma value. 
# Values are integer>=0. The default=25.
# 
# -d dim ... DIM are vignette region boundary dimensions relative to the
# size of the input image expressed as Width,Height in percent or pixels. 
# One or two values may be supplied. If only one, then it will be used for 
# the second dimension if needed. Values are integers>0. If percent, then 
# add % symbol as Width%[,Height%]. Vignette dimensions must not be larger  
# than the image dimensions or 100%. The default=90%.
# 
# -s shape ... SHAPE is the shape of the vignette region boundary. The choices 
# are: rectangle (r), roundrectangle (rr), ellipse (e) or circle (c). 
# The default=roundrectangle.
# 
# -r round ... ROUND is the amount of rounding for roundrectangle. Values are
# integers>0. The default=2*amount.
# 
# -c color ... COLOR is the vignette color. Any opaque IM color is allowed 
# or "none" for a transparency vignette effect, The default=black.
# 
# -m method ... METHOD is the vignette color compose method. Any IM compose 
# method is allowed. The default=multiply.
# 
# -i ... INVERT the vignette effect from outside to inside.
# 
# 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 up defaults
amount=25				# vignette amount; blur sigma
dim="90%"				# vignette transition region boundary; size compared to image % or pixels
shape="roundrectangle"	# rectangle, roundrectangle, ellipse, circle
round=""				# size of rounding for roundrectangle; default=2*amount
color="black"			# vignette color (not white)
method="multiply"		# vignette color compose method
invert="no"				# invert vignette to inside rather than outside dim

# 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 15 ]
	then
	errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---"
else
	while [ $# -gt 0 ]
		do
			# get parameter values
			case "$1" in
		     -help|-h)    # help information
					   echo ""
					   usage2
					   exit 0
					   ;;
				-a)    # get amount
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID AMOUNT SPECIFICATION ---"
					   checkMinus "$1"
					   amount=`expr "$1" : '\([0-9]*\)'`
					   [ "$amount" = "" ] && errMsg "--- AMOUNT=$amount MUST BE AN INTEGER ---"
					   ;;
				-d)    # get dim
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID DIM SPECIFICATION ---"
					   checkMinus "$1"
					   dim=`expr "$1" : '\([%0-9]*,*[%0-9]*\)'`
					   [ "$dim" = "" ] && errMsg "--- DIM=$dim MUST BE A COMMA SEPARATED PAIR OF NON-NEGATIVE INTEGERS (with no sign) ---"
					   ;;
				-s)    # shape
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID SHAPE SPECIFICATION ---"
					   checkMinus "$1"
					   shape="$1"
					   shape=`echo "$shape" | tr "[:upper:]" "[:lower:]"`
					   case "$shape" in 
							rectangle|r) shape="rectangle";;
							roundrectangle|rr) shape="roundrectangle";;
							ellipse|e) shape="ellipse";;
							circle|c) shape="circle";;
							*) errMsg "--- SHAPE=$shape IS AN INVALID VALUE ---" 
						esac
					   ;;
				-r)    # get round
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID ROUND SPECIFICATION ---"
					   checkMinus "$1"
					   round=`expr "$1" : '\([0-9]*\)'`
					   [ "$round" = "" ] && errMsg "--- ROUND=$round MUST BE AN INTEGER ---"
					   test1=`echo "$round < 0" | bc`
					   [ $test1 -eq 1 ] && errMsg "--- ROUND=$round MUST BE A NON-NEGATIVE INTEGER (with no sign) ---"
					   ;;
				-c)    # get color
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID COLOR SPECIFICATION ---"
					   checkMinus "$1"
					   color="$1"
					   ;;
				-m)    # get method
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID METHOD SPECIFICATION ---"
					   checkMinus "$1"
					   method="$1"
					   ;;
				-i)    # get invert
					   invert="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
	infile="$1"
	outfile="$2"
fi

# test that infile provided
[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED"

# test that outfile provided
[ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED"


# setup temp files
tmpA1="$dir/vignette2_1_$$.mpc"
tmpB1="$dir/vignette2_1_$$.cache"
trap "rm -f $tmpA1 $tmpB1;" 0
trap "rm -f $tmpA1 $tmpB1; exit 1" 1 2 3 15
#trap "rm -f $tmpA1 $tmpB1; 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`

# colorspace RGB and sRGB swapped between 6.7.5.5 and 6.7.6.7 
# though probably not resolved until the latter
# then -colorspace gray changed to linear between 6.7.6.7 and 6.7.8.2 
# then -separate converted to linear gray channels between 6.7.6.7 and 6.7.8.2,
# though probably not resolved until the latter
# so -colorspace HSL/HSB -separate and -colorspace gray became linear
# but we need to use -set colorspace RGB before using them at appropriate times
# so that results stay as in original script
# The following was determined from various version tests using vignette2
# with IM 6.7.4.10, 6.7.6.10, 6.7.9.1
if [ "$im_version" -lt "06070607" -o "$im_version" -gt "06070707" ]; then
	setcspace="-set colorspace RGB"
else
	setcspace=""
fi
# no need for setcspace for grayscale or channels after 6.8.5.4
if [ "$im_version" -gt "06080504" ]; then
	setcspace=""
fi


# test input image
convert -quiet "$infile" +repage "$tmpA1" ||
	errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---"


# get image dimensions for later cropping as input is padded to square, even dimensions
ww=`identify -ping -format "%w" $tmpA1`
hh=`identify -ping -format "%h" $tmpA1`

cx=`convert xc: -format "%[fx:$ww/2]" info:`
cy=`convert xc: -format "%[fx:$hh/2]" info:`

test=`echo "$dim" | grep "%"`
if [ "$test" = "" ]; then
	units="pixels"
else
	units="percent"
fi
dim=`echo "$dim" | sed 's/%//g'`
dimx=`echo "$dim" | cut -d, -f1`
dimy=`echo "$dim" | cut -d, -f2`

if [ "$round" = "" ]; then
	round=$((2*amount))
fi

if [ "$units" = "percent" ]; then
	ww2=`convert xc: -format "%[fx:$dimx*$cx/100]" info:`
	hh2=`convert xc: -format "%[fx:$dimy*$cy/100]" info:`
	# test if vignette dim <= image size
	[ $dimx -gt 100 ] && echo  "--- VIGNETTE WIDTH MUST NOT BE LARGER THAN IMAGE WIDTH ---"
	[ $dimy -gt 100 ] && echo  "--- VIGNETTE HEIGHT MUST NOT BE LARGER THAN IMAGE HEIGHT ---"
	
else
	ww2=$cx
	hh2=$cy
	# test if vignette dim <= image size
	[ $dimx -gt $ww ] && echo  "--- VIGNETTE WIDTH MUST NOT BE LARGER THAN IMAGE WIDTH ---"
	[ $dimy -gt $hh ] && echo  "--- VIGNETTE HEIGHT MUST NOT BE LARGER THAN IMAGE HEIGHT ---"
fi
#echo "ww=$ww; hh=$hh; cx=$cx; cy=$cy; units=$units; dimx=$dimx; dimy=$dimy; rx=$rx; ry=$ry; ww2=$ww2; hh2=$hh2"

if [ "$shape" = "rectangle" ]; then
	args="-$ww2,-$hh2 $ww2,$hh2"
elif [ "$shape" = "roundrectangle" ]; then
	args="-$ww2,-$hh2 $ww2,$hh2 $round,$round"
elif [ "$shape" = "ellipse" ]; then
	args="0,0 $ww2,$hh2 0,360"
elif [ "$shape" = "circle" ]; then
	rad=`convert xc: -format "%[fx:min($ww2,$hh2)]" info:`
	args="0,0 $rad,0"
fi
#echo "shape=$shape args=$args"

if [ "$invert" = "yes" ]; then 
	inversion="-negate -virtual-pixel black"
else
	inversion="-virtual-pixel white"
fi

if [ "$color" = "none" -o "$color" = "transparent" ]; then 
	convert $tmpA1 \
		\( -clone 0 -fill white -colorize 100% -fill black \
			-draw "translate $cx,$cy $shape $args" -negate -blur 0x$amount \) \
		-alpha off -compose copy_opacity -composite "$outfile"
else
convert $tmpA1 \
	\( -clone 0 -fill "$color" -colorize 100%  \) \
	\( -clone 0 -fill white -colorize 100% -fill black \
		-draw "translate $cx,$cy $shape $args" $inversion -blur 0x$amount \) \
	$setcspace -compose $method -composite "$outfile"
fi

exit 0