summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPepper <pepper@scannerjammer.com>2015-02-26 02:03:24 -0500
committerPepper <pepper@scannerjammer.com>2015-02-26 02:03:24 -0500
commitd489eb062653a2726d4fbd42bfd34835dc770d4b (patch)
treeb6f73ee095d713feb7b656e9d178126e83a62f63
first
-rw-r--r--.gitignore2
-rwxr-xr-x3Drotate873
-rwxr-xr-xbevelborder295
-rwxr-xr-xbreaker.py394
-rwxr-xr-xdb.py31
-rwxr-xr-xgradient.py257
-rwxr-xr-xgrid241
-rw-r--r--imbreak_main.js113
-rw-r--r--imgradient_index.html532
-rwxr-xr-ximgrid.py356
-rw-r--r--imgrid_main.js121
-rwxr-xr-xpbserver.py182
-rw-r--r--s3.py618
-rw-r--r--s3config.py3
-rw-r--r--test.sh6
15 files changed, 4024 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f739444
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+./s3config.py
+./db.py
diff --git a/3Drotate b/3Drotate
new file mode 100755
index 0000000..227b92d
--- /dev/null
+++ b/3Drotate
@@ -0,0 +1,873 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 8/18/2007 .......... revised 11/26/2011
+#
+# USAGE: 3Drotate option=value infile outfile
+# USAGE: 3Drotate [-h or -help]
+#
+# OPTIONS: any one or more
+#
+# pan value rotation about image vertical centerline;
+# -180 to +180 (deg); default=0
+# tilt value rotation about image horizontal centerline;
+# -180 to +180 (deg); default=0
+# roll value rotation about the image center;
+# -180 to +180 (deg); default=0
+# pef value perspective exaggeration factor;
+# 0 to 3.19; default=1
+# idx value +/- pixel displacement in rotation point right/left
+# in input from center; default=0
+# idy value +/- pixel displacement in rotation point down/up
+# in input from center; default=0
+# odx value +/- pixel displacement in rotation point right/left
+# in output from center; default=0
+# ody value +/- pixel displacement in rotation point down/up
+# in output from center; default=0
+# zoom value output zoom factor; where value > 1 means zoom in
+# and < -1 means zoom out; value=1 means no change
+# bgcolor value the background color value; any valid IM image
+# color specification (see -fill); default is black
+# skycolor value the sky color value; any valid IM image
+# color specification (see -fill); default is black
+# auto c center bounding box in output
+# (odx and ody ignored)
+# auto zc zoom to fill and center bounding box in output
+# (odx, ody and zoom ignored)
+# auto out creates an output image of size needed to hold
+# the transformed image; (odx, ody and zoom ignored)
+# vp value virtual-pixel method; any valid IM virtual-pixel method;
+# default=background
+#
+###
+#
+# NAME: 3DROTATE
+#
+# PURPOSE: To apply a perspective distortion to an image by providing rotation angles,
+# zoom, offsets, background color, perspective exaggeration and auto zoom/centering.
+#
+# DESCRIPTION: 3DROTATE applies a perspective distortion to an image
+# by providing any combination of three optional rotation angle:
+# pan, tilt and roll with optional offsets and zoom and with an optional
+# control of the perspective exaggeration. The image is treated as if it
+# were painted on the Z=0 ground plane. The picture plane is then rotated
+# and then perspectively projected to a camera located a distance equal to
+# the focal length above the ground plane looking straight down along
+# the -Z direction.
+#
+#
+# ARGUMENTS:
+#
+# PAN is a rotation of the image about its vertical
+# centerline -180 to +180 degrees. Positive rotations turn the
+# right side of the image away from the viewer and the left side
+# towards the viewer. Zero is no rotation. A PAN of +/- 180 deg
+# achieves the same results as -flip.
+#
+# TILT is a rotation of the image about its horizontal
+# centerline -180 to +180 degrees. Positive rotations turn the top
+# of the image away from the viewer and the bottom towards the
+# viewer. Zero is no rotation. A TILT of +/- 180 deg
+# achieves the same results as -flop.
+#
+# ROLL (like image rotation) is a rotation in the plane of the
+# the image -180 to +180 degrees. Positive values are clockwise
+# and negative values are counter-clockwise. Zero is no rotation.
+# A ROLL of any angle achieves the same results as -rotate.
+#
+# PAN, TILT and ROLL are order dependent. If all three are provided,
+# then they will be done in whatever order specified.
+#
+# PEF is the perspective exaggeration factor. It ranges from 0 to 3.19.
+# A normal perspective is achieved with the default of 1. As PEF is
+# increased from 1, the perspective effect moves towards that of
+# a wide angle lens (more distortion). If PEF is decreased from 1
+# the perspective effect moves towards a telephoto lens (less
+# distortion). PEF of 0.5 achieves an effect close to no perspective
+# distortion. As pef gets gets larger than some value which depends
+# upon the larger the pan, tilt and roll angles become, one reaches
+# a point where some parts of the picture become so distorted that
+# they wrap around and appear above the "horizon"
+#
+# IDX is the a pixel displacement of the rotation point in the input image
+# from the image center. Positive values shift to the right along the
+# sample direction; negative values shift to the left. The default=0
+# corresponds to the image center.
+#
+# IDY is the a pixel displacement of the rotation point in the input image
+# from the image center. Positive values shift to downward along the
+# line direction; negative values shift upward. The default=0
+# corresponds to the image center.
+#
+# ODX is the a pixel displacement from the center of the output image where
+# one wants the corresponding input image rotation point to appear.
+# Positive values shift to the right along the sample direction; negative
+# values shift to the left. The default=0 corresponds to the output image center.
+#
+# ODY is the a pixel displacement from the center of the output image where
+# one wants the corresponding input image rotation point to appear.
+# Positive values shift downward along the sample direction; negative
+# values shift upward. The default=0 corresponds to the output image center.
+#
+# ZOOM is the output image zoom factor. Values > 1 (zoomin) cause the image
+# to appear closer; whereas values < 1 (zoomout) cause the image to
+# appear further away.
+#
+# BGCOLOR is the color of the background to use to fill where the output image
+# is outside the area of the perspective of the input image. See the IM function
+# -fill for color specifications. Note that when using rgb(r,g,b), this must be
+# enclosed in quotes after the equal sign.
+#
+# SKYCOLOR is the color to use in the 'sky' area above the perspective 'horizon'.
+# See the IM function -fill for color specifications. Note that when using
+# rgb(r,g,b), this must be enclosed in quotes after the equal sign.
+#
+# AUTO can be either c, zc or out. If auto is c, then the resulting perspective
+# of the input image will have its bounding box centered in the output image
+# whose size will be the same as the input image. If
+# auto is zc, then the resulting perspective of the input image will have its
+# bounding box zoomed to fill its largest dimension to match the size of the
+# the input image and the other dimension will be centered in the output. If
+# auto is out, then the output image will be made as large or as small as
+# needed to just fill out the transformed input image. If any of these are
+# present, then the arguments OSHIFTX, OSHIFTY are ignored.
+#
+# VP is the virtual-pixel method, which allows the image to be extended outside
+# its bounds. For example, vp=background, then the background color is used to
+# fill the area in the output image which is outside the perspective view of
+# the input image. If vp=tile, then the perspective view will be tiled to fill
+# the output image.
+#
+# NOTE: The output image size will be the same as the input image size due
+# to current limitations on -distort Perspective.
+#
+# 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 value
+# rotation angles and rotation matrix
+pan=0
+tilt=0
+roll=0
+R0=(1 0 0)
+R1=(0 1 0)
+R2=(0 0 1)
+
+# scaling output only
+sx=1
+sy=1
+
+# offset du,dv = output; relative to center of image
+du=0
+dv=0
+
+# offset di,dj = input; relative to center of image
+di=0
+dj=0
+
+# perspective exaggeration factor
+pef=1
+
+# zoom
+zoom=1
+
+# background color
+bgcolor="black"
+
+# sky color
+skycolor="black"
+
+# virtual-pixel method
+vp="background"
+
+# set directory for temporary files
+dir="." # suggestions are dir="." or dir="/tmp"
+
+# compute pi
+pi=`echo "scale=10; 4*a(1)" | bc -l`
+
+
+# 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 -n '/^###/q; /^#/!q; s/^#//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME"
+ }
+usage2()
+ {
+ echo >&2 ""
+ echo >&2 "$PROGNAME:" "$@"
+ sed >&2 -n '/^######/q; /^#/!q; s/^#*//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME"
+ }
+
+# function to report error messages, usage and exit
+errMsg()
+ {
+ echo ""
+ echo $1
+ echo ""
+ usage1
+ exit 1
+ }
+
+# function to do dot product of 2 three element vectors
+function DP3
+ {
+ V0=($1)
+ V1=($2)
+ DP=`echo "scale=10; (${V0[0]} * ${V1[0]}) + (${V0[1]} * ${V1[1]}) + (${V0[2]} * ${V1[2]})" | bc`
+ }
+
+# function to do 3x3 matrix multiply M x N where input are rows of each matrix; M1 M2 M3 N1 N2 N3
+function MM3
+ {
+ [ $# -ne 6 ] && errMsg "--- NOT A VALID SET OF MATRIX PARAMETERS ---"
+ M0=($1)
+ M1=($2)
+ M2=($3)
+ N0=($4)
+ N1=($5)
+ N2=($6)
+ [ ${#M0[*]} -ne 3 -a ${#M1[*]} -ne 3 -a ${#M2[*]} -ne 3 -a ${#N0[*]} -ne 3 -a ${#N1[*]} -ne 3 -a ${#N2[*]} -ne 3 ] && errMsg "--- NOT A VALID SET OF MATRIX ROWS ---"
+ # extract columns n from rows N
+ n0=(${N0[0]} ${N1[0]} ${N2[0]})
+ n1=(${N0[1]} ${N1[1]} ${N2[1]})
+ n2=(${N0[2]} ${N1[2]} ${N2[2]})
+ DP3 "${M0[*]}" "${n0[*]}"
+ P00=$DP
+ DP3 "${M0[*]}" "${n1[*]}"
+ P01=$DP
+ DP3 "${M0[*]}" "${n2[*]}"
+ P02=$DP
+ DP3 "${M1[*]}" "${n0[*]}"
+ P10=$DP
+ DP3 "${M1[*]}" "${n1[*]}"
+ P11=$DP
+ DP3 "${M1[*]}" "${n2[*]}"
+ P12=$DP
+ DP3 "${M2[*]}" "${n0[*]}"
+ P20=$DP
+ DP3 "${M2[*]}" "${n1[*]}"
+ P21=$DP
+ DP3 "${M2[*]}" "${n2[*]}"
+ P22=$DP
+ P0=($P00 $P01 $P02)
+ P1=($P10 $P11 $P12)
+ P2=($P20 $P21 $P22)
+ }
+
+# function to project points from input to output domain
+function forwardProject
+ {
+ ii=$1
+ jj=$2
+ numu=`echo "scale=10; ($P00 * $ii) + ($P01 * $jj) + $P02" | bc`
+ numv=`echo "scale=10; ($P10 * $ii) + ($P11 * $jj) + $P12" | bc`
+ den=`echo "scale=10; ($P20 * $ii) + ($P21 * $jj) + $P22" | bc`
+ uu=`echo "scale=0; $numu / $den" | bc`
+ vv=`echo "scale=0; $numv / $den" | bc`
+ }
+
+# function to project points from input to output domain
+function inverseProject
+ {
+ uu=$1
+ vv=$2
+ numi=`echo "scale=10; ($Q00 * $uu) + ($Q01 * $vv) + $Q02" | bc`
+ numj=`echo "scale=10; ($Q10 * $uu) + ($Q11 * $vv) + $Q12" | bc`
+ den=`echo "scale=10; ($Q20 * $uu) + ($Q21 * $vv) + $Q22" | bc`
+ ii=`echo "scale=0; $numi / $den" | bc`
+ jj=`echo "scale=0; $numj / $den" | bc`
+ }
+
+# function to invert a 3 x 3 matrix using method of adjoint
+# inverse is the transpose of the matrix of cofactors divided by the determinant
+function M3inverse
+ {
+ m00=$1
+ m01=$2
+ m02=$3
+ m10=$4
+ m11=$5
+ m12=$6
+ m20=$7
+ m21=$8
+ m22=$9
+ c00=`echo "scale=10; ($m11 * $m22) - ($m21 * $m12)" | bc`
+ c01=`echo "scale=10; ($m20 * $m12) - ($m10 * $m22)" | bc`
+ c02=`echo "scale=10; ($m10 * $m21) - ($m20 * $m11)" | bc`
+ c10=`echo "scale=10; ($m21 * $m02) - ($m01 * $m22)" | bc`
+ c11=`echo "scale=10; ($m00 * $m22) - ($m20 * $m02)" | bc`
+ c12=`echo "scale=10; ($m20 * $m01) - ($m00 * $m21)" | bc`
+ c20=`echo "scale=10; ($m01 * $m12) - ($m11 * $m02)" | bc`
+ c21=`echo "scale=10; ($m10 * $m02) - ($m00 * $m12)" | bc`
+ c22=`echo "scale=10; ($m00 * $m11) - ($m10 * $m01)" | bc`
+ det=`echo "scale=10; ($m00 * $c00) + ($m01 * $c01) + ($m02 * $c02)" | bc`
+ idet=`echo "scale=10; 1 / $det" | bc`
+ Q00=`echo "scale=10; $c00 * $idet" | bc`
+ Q01=`echo "scale=10; $c10 * $idet" | bc`
+ Q02=`echo "scale=10; $c20 * $idet" | bc`
+ Q10=`echo "scale=10; $c01 * $idet" | bc`
+ Q11=`echo "scale=10; $c11 * $idet" | bc`
+ Q12=`echo "scale=10; $c21 * $idet" | bc`
+ Q20=`echo "scale=10; $c02 * $idet" | bc`
+ Q21=`echo "scale=10; $c12 * $idet" | bc`
+ Q22=`echo "scale=10; $c22 * $idet" | bc`
+ Q0=($Q00 $Q01 $Q02)
+ Q1=($Q10 $Q11 $Q12)
+ Q2=($Q20 $Q21 $Q22)
+ }
+
+# function to test if entry is floating point number
+function testFloat
+ {
+ test1=`expr "$1" : '^[0-9][0-9]*$'` # counts same as above but preceeded by plus or minus
+ test2=`expr "$1" : '^[+-][0-9][0-9]*$'` # counts one or more digits
+ test3=`expr "$1" : '^[0-9]*[\.][0-9]*$'` # counts 0 or more digits followed by period followed by 0 or more digits
+ test4=`expr "$1" : '^[+-][0-9]*[\.][0-9]*$'` # counts same as above but preceeded by plus or minus
+ floatresult=`expr $test1 + $test2 + $test3 + $test4`
+# [ $floatresult = 0 ] && errMsg "THE ENTRY $1 IS NOT A FLOATING POINT NUMBER"
+ }
+
+# get input image size
+function imagesize
+ {
+ width=`identify -format %w $tmpA`
+ height=`identify -format %h $tmpA`
+ }
+
+# 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
+ -h|-help) # help information
+ echo ""
+ usage2
+ exit 0
+ ;;
+ -) # STDIN and end of arguments
+ break
+ ;;
+ -*) # any other - argument
+ errMsg "--- UNKNOWN OPTION ---"
+ ;;
+ pan[=]*) # pan angle
+ arg="$1="
+ pan=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ pan=`echo "$pan" | sed 's/^[+]\(.*\)$/\1/'`
+ # pantest>0 if floating point number; otherwise pantest=0
+ testFloat "$pan"; pantest=$floatresult
+ pantestA=`echo "$pan < - 180" | bc`
+ pantestB=`echo "$pan > 180" | bc`
+ [ $pantest -eq 0 ] && errMsg "PAN=$pan IS NOT A NUMBER"
+ [ $pantestA -eq 1 -o $pantestB -eq 1 ] && errMsg "PAN=$pan MUST BE GREATER THAN -180 AND LESS THAN +180"
+ panang=`echo "scale=10; $pi * $pan / 180" | bc`
+ sinpan=`echo "scale=10; s($panang)" | bc -l`
+ sinpanm=`echo "scale=10; - $sinpan" | bc`
+ cospan=`echo "scale=10; c($panang)" | bc -l`
+ Rp0=($cospan 0 $sinpan)
+ Rp1=(0 1 0)
+ Rp2=($sinpanm 0 $cospan)
+ # do matrix multiply to get new rotation matrix
+ MM3 "${Rp0[*]}" "${Rp1[*]}" "${Rp2[*]}" "${R0[*]}" "${R1[*]}" "${R2[*]}"
+ R0=(${P0[*]})
+ R1=(${P1[*]})
+ R2=(${P2[*]})
+ ;;
+ tilt[=]*) # tilt angle
+ arg="$1="
+ tilt=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ tilt=`echo "$tilt" | sed 's/^[+]\(.*\)$/\1/'`
+ # tilttest>0 if floating point number; otherwise tilttest=0
+ testFloat "$tilt"; tilttest=$floatresult
+ tilttestA=`echo "$tilt < - 180" | bc`
+ tilttestB=`echo "$tilt > 180" | bc`
+ [ $tilttest -eq 0 ] && errMsg "tilt=$tilt IS NOT A NUMBER"
+ [ $tilttestA -eq 1 -o $tilttestB -eq 1 ] && errMsg "TILT=$tilt MUST BE GREATER THAN -180 AND LESS THAN +180"
+ tiltang=`echo "scale=10; $pi * $tilt / 180" | bc`
+ sintilt=`echo "scale=10; s($tiltang)" | bc -l`
+ sintiltm=`echo "scale=10; - $sintilt" | bc`
+ costilt=`echo "scale=10; c($tiltang)" | bc -l`
+ Rt0=(1 0 0)
+ Rt1=(0 $costilt $sintilt)
+ Rt2=(0 $sintiltm $costilt)
+ # do matrix multiply to get new rotation matrix
+ MM3 "${Rt0[*]}" "${Rt1[*]}" "${Rt2[*]}" "${R0[*]}" "${R1[*]}" "${R2[*]}"
+ R0=(${P0[*]})
+ R1=(${P1[*]})
+ R2=(${P2[*]})
+ ;;
+ roll[=]*) # roll angle
+ arg="$1="
+ roll=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ roll=`echo "$roll" | sed 's/^[+]\(.*\)$/\1/'`
+ # rolltest>0 if floating point number; otherwise rolltest=0
+ testFloat "$roll"; rolltest=$floatresult
+ rolltestA=`echo "$roll < - 180" | bc`
+ rolltestB=`echo "$roll > 180" | bc`
+ [ $rolltest -eq 0 ] && errMsg "roll=$roll IS NOT A NUMBER"
+ [ $rolltestA -eq 1 -o $rolltestB -eq 1 ] && errMsg "ROLL=$roll MUST BE GREATER THAN -180 AND LESS THAN +180"
+ rollang=`echo "scale=10; $pi * $roll / 180" | bc`
+ sinroll=`echo "scale=10; s($rollang)" | bc -l`
+ sinrollm=`echo "scale=10; - $sinroll" | bc`
+ cosroll=`echo "scale=10; c($rollang)" | bc -l`
+ Rr0=($cosroll $sinroll 0)
+ Rr1=($sinrollm $cosroll 0)
+ Rr2=(0 0 1)
+ # do matrix multiply to get new rotation matrix
+ MM3 "${Rr0[*]}" "${Rr1[*]}" "${Rr2[*]}" "${R0[*]}" "${R1[*]}" "${R2[*]}"
+ R0=(${P0[*]})
+ R1=(${P1[*]})
+ R2=(${P2[*]})
+ ;;
+ pef[=]*) # pef
+ arg="$1="
+ pef=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ pef=`echo "$pef" | sed 's/^[+]\(.*\)$/\1/'`
+ # peftest>0 if floating point number; otherwise peftest=0
+ testFloat "$pef"; peftest=$floatresult
+ peftestA=`echo "$pef < 0" | bc`
+ peftestB=`echo "$pef > 3.19" | bc`
+ [ $peftest -eq 0 ] && errMsg "PEF=$pef IS NOT A NUMBER"
+ ;;
+ idx[=]*) # input x shift
+ arg="$1="
+ di=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ di=`echo "$di" | sed 's/^[+]\(.*\)$/\1/'`
+ # ditest>0 if floating point number; otherwise ditest=0
+ testFloat "$di"; ditest=$floatresult
+ [ $ditest -eq 0 ] && errMsg "ISHIFTX=$di IS NOT A NUMBER"
+ ;;
+ idy[=]*) # input y shift
+ arg="$1="
+ dj=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ dj=`echo "$dj" | sed 's/^[+]\(.*\)$/\1/'`
+ # djtest>0 if floating point number; otherwise ditest=0
+ testFloat "$dj"; djtest=$floatresult
+ [ $djtest -eq 0 ] && errMsg "ISHIFTY=$dj IS NOT A NUMBER"
+ ;;
+ odx[=]*) # output x shift
+ arg="$1="
+ du=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ du=`echo "$du" | sed 's/^[+]\(.*\)$/\1/'`
+ # dutest>0 if floating point number; otherwise ditest=0
+ testFloat "$du"; dutest=$floatresult
+ [ $dutest -eq 0 ] && errMsg "OSHIFTX=$du IS NOT A NUMBER"
+ ;;
+ ody[=]*) # output y shift
+ arg="$1="
+ dv=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ dv=`echo "$dv" | sed 's/^[+]\(.*\)$/\1/'`
+ # dvtest>0 if floating point number; otherwise ditest=0
+ testFloat "$dv"; dvtest=$floatresult
+ [ $dvtest -eq 0 ] && errMsg "OSHIFTY=$dv IS NOT A NUMBER"
+ ;;
+ zoom[=]*) # output zoom
+ arg="$1="
+ zoom=`echo "$arg" | cut -d= -f2`
+ # function bc does not seem to like numbers starting with + sign, so strip off
+ zoom=`echo "$zoom" | sed 's/^[+]\(.*\)$/\1/'`
+ # zoomtest>0 if floating point number; otherwise peftest=0
+ testFloat "$zoom"; zoomtest=$floatresult
+ zoomtest=`echo "$zoom < 1 && $zoom > -1" | bc`
+ [ $zoomtest -eq 1 ] && errMsg "ZOOM=$zoom MUST BE GREATER THAN 1 OR LESS THAN -1"
+ ;;
+ bgcolor[=]*) # output background color
+ arg="$1="
+ bgcolor=`echo "$arg" | cut -d= -f2`
+ ;;
+ skycolor[=]*) # output sky color
+ arg="$1="
+ skycolor=`echo "$arg" | cut -d= -f2`
+ ;;
+ vp[=]*) # virtual pixel method
+ arg="$1="
+ vp=`echo "$arg" | cut -d= -f2`
+ [ "$vp" != "background" -a "$vp" != "dither" -a "$vp" != "edge" -a "$vp" != "mirror" -a "$vp" != "random" -a "$vp" != "tile" -a "$vp" != "transparent" ] && errMsg "VP=$vp IS NOT A VALID VALUE"
+ ;;
+ auto[=]*) # output background color
+ arg="$1="
+ auto=`echo "$arg" | cut -d= -f2`
+ [ "$auto" != "c" -a "$auto" != "zc" -a "$auto" != "out" ] && errMsg "AUTO=$auto IS NOT A VALID VALUE"
+ ;;
+ *[=]*) # not valid
+ errMsg "$1 IS NOT A VALID ARGUMENT"
+ ;;
+ *) # end of arguments
+ break
+ ;;
+ esac
+ shift # next option
+ done
+ #
+ # get infile and outfile
+ infile=$1
+ outfile=$2
+fi
+
+# setup temporary images and auto delete upon exit
+# use mpc/cache to hold input image temporarily in memory
+tmpA="$dir/3Drotate_$$.mpc"
+tmpB="$dir/3Drotate_$$.cache"
+trap "rm -f $tmpA $tmpB; exit 0" 0
+trap "rm -f $tmpA $tmpB; exit 1" 1 2 3 15
+
+# test that infile provided
+[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED"
+# test that outfile provided
+[ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED"
+
+if convert -quiet -regard-warnings "$infile" +repage "$tmpA"
+ then
+ [ "$pef" = "" ] && pef=1
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---"
+fi
+
+# get input image width and height
+imagesize
+maxwidth=`expr $width - 1`
+maxheight=`expr $height - 1`
+
+# deal with auto adjustments to values
+if [ "$auto" = "zc" ]
+ then
+ du=0
+ dv=0
+ zoom=1
+elif [ "$auto" = "c" ]
+ then
+ du=0
+ dv=0
+fi
+
+# convert offsets of rotation point to relative to pixel 0,0
+di=`echo "scale=10; ($di + (($width - 1) / 2)) / 1" | bc`
+dj=`echo "scale=10; ($dj + (($height - 1) / 2)) / 1" | bc`
+du=`echo "scale=10; $du / 1" | bc`
+dv=`echo "scale=10; $dv / 1" | bc`
+
+# convert zoom to scale factors
+if [ `echo "$zoom >= 1" | bc` -eq 1 ]
+ then
+ sx=`echo "scale=10; 1 / $zoom" | bc`
+ sy=$sx
+elif [ `echo "$zoom <= -1" | bc` -eq 1 ]
+ then
+ sx=`echo "scale=10; - $zoom / 1" | bc`
+ sy=$sx
+fi
+
+# Consider the picture placed on the Z=0 plane and the camera a distance
+# Zc=f above the picture plane looking straight down at the image center.
+# Now the perspective equations (in 3-D) are defined as (x,y,f) = M (X',Y',Z'),
+# where the camera orientation matrix M is the identity matrix but with M22=-1
+# because the camera is looking straight down along -Z.
+# Thus a reflection transformation relative to the ground plane coordinates.
+# Let the camera position Zc=f=(sqrt(ins*ins + inl*inl)) / ( 2 tan(fov/2) )
+# Now we want to rotate the ground points corresponding to the picture corners.
+# The basic rotation is (X',Y',Z') = R (X,Y,0), where R is the rotation matrix
+# involving pan, tilt and roll.
+# But we need to convert (X,Y,0) to (X,Y,1) and also to offset for Zc=f
+# First we note that (X,Y,0) = (X,Y,1) - (0,0,1)
+# Thus the equation becomes (x,y,f) = M {R [(X,Y,1) - (0,0,1)] - (0,0,Zc)} = MT (X,Y,1)
+# But R [(X,Y,1) - (0,0,1)] = R [II (X,Y,1) - S (X,Y,1)] = R (II-S) (X,Y,1), where
+# II is the identity matrix and S is an all zero matrix except for S22=1.
+# Thus (II-S) is the identity matrix with I22=0 and
+# RR = R (II-S) is just R with the third column all zeros.
+# Thus we get (x,y,f) = M {RR (X,Y,1) - (0,0,Zc)}.
+# But M {RR (X,Y,1) - (0,0,Zc)} = M {RR(X,Y,1) - D (X,Y,1)}, where
+# D is an all zero matrix with D22 = Zc = f.
+# So that we get M (RR-D) (X,Y,1) = MT (X,Y,1), where
+# where T is just R with the third column (0,0,-f), i.e. T02=0, T12=0, T22=-f
+# But we need to allow for scaling and offset of the output coordinates and
+# conversion from (x,y,f) to (u,v,1)=O and conversion of input coordinates
+# from (X,Y,1) to (i,j,1)=I.
+# Thus the forward transformation becomes AO=MTBI or O=A'MTBI or O=PI,
+# where prime means inverse.
+# However, to do the scaling of the output correctly, need to offset by the input
+# plus output offsets, then scale, which is all put into A'.
+# Thus the forward transformation becomes AO=MTBI or O=A'MTBI where A'=Ai
+# but we will merge A'M into Aim
+# Thus the inverse transform becomes
+# I=QO where Q=P'
+# A=output scaling, offset and conversion matrix
+# B=input offset and conversion matrix (scaling only needs to be done in one place)
+# M=camera orientation matrix
+# R=image rotation matrix Rroll Rtilt Rpan
+# T=matrix that is R but R33 offset by f + 1
+# O=output coords vector (i,j,1)
+# I=input coords vector (u,v,1)=(is,il,1)
+# P=forward perspective transformation matrix
+# Q=inverse perspective transformation matrix
+#
+# For a 35 mm camera whose film format is 36mm wide and 24mm tall, when the focal length
+# is equal to the diagonal, the field of view is 53.13 degrees and this is
+# considered a normal view equivalent to the human eye.
+# See http://www.panoramafactory.com/equiv35/equiv35.html
+# Max limit on dfov is 180 degrees (pef=3.19) where get single line like looking at picture on edge.
+# Above this limit the picture becomes like the angles get reversed.
+# Min limit on dfov seems to be slightly greater than zero degrees.
+# Practical limits on dfov depend upon orientation angles.
+# For tilt=45, this is about 2.5 dfov (pef=2.5). Above this, some parts of the picture
+# that are cut off at the bottom, get wrapped and stretched in the 'sky'.
+
+dfov=`echo "scale=10; 180 * a(36/24) / $pi" | bc -l`
+if [ "$pef" = "" ]
+ then
+ pfact=1
+elif [ "$pef" = "0" ]
+ then
+ pfact=`echo "scale=10; 0.01 / $dfov" | bc`
+else
+ pfact=$pef
+fi
+#maxpef=`echo "scale=5; 180 / $dfov" | bc`
+#echo "maxpef=$maxpef"
+
+#compute new field of view based upon pef (pfact)
+dfov=`echo "scale=10; $pfact * $dfov" | bc`
+dfov2=`echo "scale=10; $dfov / 2" | bc`
+arg=`echo "scale=10; $pi * $dfov2 / 180" | bc`
+sfov=`echo "scale=10; s($arg)" | bc -l`
+cfov=`echo "scale=10; c($arg)" | bc -l`
+tfov=`echo "scale=10; $sfov / $cfov" | bc -l`
+#echo "tfov=$tfov"
+
+# calculate focal length in same units as wall (picture) using dfov
+diag=`echo "scale=10; sqrt(($width * $width) + ($height * $height))" | bc`
+focal=`echo "scale=10; ($diag / (2 * $tfov))" | bc -l`
+#echo "focal=$focal"
+
+# calculate forward transform matrix Q
+
+# define the input offset and conversion matrix
+dim=`echo "scale=10; - $di" | bc`
+B0=(1 0 $dim)
+B1=(0 -1 $dj)
+B2=(0 0 1)
+
+# define the output scaling, offset and conversion matrix inverse Ai and merge with M
+# to become Aim
+#A0=($sx 0 $sx*(-$du-$di))
+#A1=(0 -$sy $sy*($dv+$dj))
+#A2=(0 0 -$focal)
+#M0=(1 0 0)
+#M1=(0 1 0)
+#M2=(0 0 -1)
+aim00=`echo "scale=10; 1 / $sx" | bc`
+aim02=`echo "scale=10; -($sx * ($di + $du)) / ($sx * $focal)" | bc`
+aim11=`echo "scale=10; -1 / $sy" | bc`
+aim12=`echo "scale=10; -($sy * ($dj + $dv)) / ($sy * $focal)" | bc`
+aim22=`echo "scale=10; -1 / $focal" | bc`
+Aim0=($aim00 0 $aim02)
+Aim1=(0 $aim11 $aim12)
+Aim2=(0 0 $aim22)
+
+# now do successive matrix multiplies from right towards left of main equation P=A'RB
+
+# convert R to T by setting T02=T12=0 and T22=-f
+focalm=`echo "scale=10; - $focal" | bc`
+T0=(${R0[0]} ${R0[1]} 0)
+T1=(${R1[0]} ${R1[1]} 0)
+T2=(${R2[0]} ${R2[1]} $focalm)
+
+# multiply T x B = P
+MM3 "${T0[*]}" "${T1[*]}" "${T2[*]}" "${B0[*]}" "${B1[*]}" "${B2[*]}"
+
+# multiply Aim x P = P
+MM3 "${Aim0[*]}" "${Aim1[*]}" "${Aim2[*]}" "${P0[*]}" "${P1[*]}" "${P2[*]}"
+
+# the resulting P matrix is now the perspective coefficients for the inverse transformation
+P00=${P0[0]}
+P01=${P0[1]}
+P02=${P0[2]}
+P10=${P1[0]}
+P11=${P1[1]}
+P12=${P1[2]}
+P20=${P2[0]}
+P21=${P2[1]}
+P22=${P2[2]}
+
+# project input corners to output domain
+#echo "UL"
+i=0
+j=0
+#echo "i,j=$i,$j"
+forwardProject $i $j
+#echo "u,v=$uu,$vv"
+u1=$uu
+v1=$vv
+#echo "UR"
+i=$maxwidth
+j=0
+#echo "i,j=$i,$j"
+forwardProject $i $j
+#echo "u,v=$uu,$vv"
+u2=$uu
+v2=$vv
+#echo "BR"
+i=$maxwidth
+j=$maxheight
+#echo "i,j=$i,$j"
+forwardProject $i $j
+#echo "u,v=$uu,$vv"
+u3=$uu
+v3=$vv
+#echo "BL"
+i=0
+j=$maxheight
+#echo "i,j=$i,$j"
+forwardProject $i $j
+#echo "u,v=$uu,$vv"
+u4=$uu
+v4=$vv
+#echo "C"
+#i=`echo "scale=10; $maxwidth / 2" | bc`
+#j=`echo "scale=10; $maxheight / 2" | bc`
+#echo "i,j=$i,$j"
+#forwardProject $i $j
+#echo "u,v=$uu,$vv"
+#u5=$uu
+#v5=$vv
+
+# unused
+: '
+# Now invert P to get Q for the inverse perspective transformation
+# Use the Method of the Adjoint Matrix = transpose of matrix of cofactors divided by the determinant
+# M3inverse $P00 $P01 $P02 $P10 $P11 $P12 $P20 $P21 $P22
+#
+# project output corners to input domain
+# UL
+#echo "UL 0,0"
+#u=$u1
+#v=$v1
+#echo "u,v=$u,$v"
+#inverseProject $u $v
+#echo "i,j=$ii,$jj"
+#echo "UR 255,0"
+#u=$u2
+#v=$v2
+#echo "u,v=$u,$v"
+#inverseProject $u $v
+#echo "i,j=$ii,$jj"
+#echo "BR 255,255"
+#u=$u3
+#v=$v3
+#echo "u,v=$u,$v"
+#inverseProject $u $v
+#echo "i,j=$ii,$jj"
+#echo "BL 0,255"
+#u=$u4
+#v=$v4
+#echo "u,v=$u,$v"
+#inverseProject $u $v
+#echo "i,j=$ii,$jj"
+#echo "C 127.5,127.5"
+#u=$u5
+#v=$v5
+#echo "u,v=$u,$v"
+#inverseProject $u $v
+#echo "i,j=$ii,$jj"
+'
+
+# deal with adjustments for auto settings
+# first get the bounding box dimensions
+uArr=($u1 $u2 $u3 $u4)
+vArr=($v1 $v2 $v3 $v4)
+index=0
+umin=1000000
+umax=-1000000
+vmin=1000000
+vmax=-1000000
+while [ $index -lt 4 ]
+ do
+ [ `echo "${uArr[$index]} < $umin" | bc` -eq 1 ] && umin=${uArr[$index]}
+ [ `echo "${uArr[$index]} > $umax" | bc` -eq 1 ] && umax=${uArr[$index]}
+ [ `echo "${vArr[$index]} < $vmin" | bc` -eq 1 ] && vmin=${vArr[$index]}
+ [ `echo "${vArr[$index]} > $vmax" | bc` -eq 1 ] && vmax=${vArr[$index]}
+ index=`expr $index + 1`
+done
+delu=`echo "scale=10; $umax - $umin + 1" | bc`
+delv=`echo "scale=10; $vmax - $vmin + 1" | bc`
+if [ "$auto" = "c" ]
+ then
+ offsetu=`echo "scale=10; ($width - $delu) / 2" | bc`
+ offsetv=`echo "scale=10; ($height - $delv) / 2" | bc`
+ u1=`echo "scale=0; $offsetu + ($u1 - $umin)" | bc`
+ v1=`echo "scale=0; $offsetv + ($v1 - $vmin)" | bc`
+ u2=`echo "scale=0; $offsetu + ($u2 - $umin)" | bc`
+ v2=`echo "scale=0; $offsetv + ($v2 - $vmin)" | bc`
+ u3=`echo "scale=0; $offsetu + ($u3 - $umin)" | bc`
+ v3=`echo "scale=0; $offsetv + ($v3 - $vmin)" | bc`
+ u4=`echo "scale=0; $offsetu + ($u4 - $umin)" | bc`
+ v4=`echo "scale=0; $offsetv + ($v4 - $vmin)" | bc`
+elif [ "$auto" = "zc" ]
+ then
+ if [ `echo "$delu > $delv" | bc` -eq 1 ]
+ then
+ del=$delu
+ offsetu=0
+ offsetv=`echo "scale=10; ($height - ($delv * $width / $delu)) / 2" | bc`
+ else
+ del=$delv
+ offsetu=`echo "scale=10; ($width - ($delu * $height / $delv)) / 2" | bc`
+ offsetv=0
+ fi
+ u1=`echo "scale=0; $offsetu + (($u1 - $umin) * $width / $del)" | bc`
+ v1=`echo "scale=0; $offsetv + (($v1 - $vmin) * $height / $del)" | bc`
+ u2=`echo "scale=0; $offsetu + (($u2 - $umin) * $width / $del)" | bc`
+ v2=`echo "scale=0; $offsetv + (($v2 - $vmin) * $height / $del)" | bc`
+ u3=`echo "scale=0; $offsetu + (($u3 - $umin) * $width / $del)" | bc`
+ v3=`echo "scale=0; $offsetv + (($v3 - $vmin) * $height / $del)" | bc`
+ u4=`echo "scale=0; $offsetu + (($u4 - $umin) * $width / $del)" | bc`
+ v4=`echo "scale=0; $offsetv + (($v4 - $vmin) * $height / $del)" | bc`
+fi
+#
+# now do the perspective distort
+if [ "$auto" = "out" ]
+ then
+ distort="+distort"
+else
+ distort="-distort"
+fi
+
+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" -lt "06030600" ]
+ then
+ convert $tmpA -virtual-pixel $vp -background $bgcolor \
+ -mattecolor $skycolor $distort Perspective \
+ "0,0 $maxwidth,0 $maxwidth,$maxheight 0,$maxheight $u1,$v1 $u2,$v2 $u3,$v3 $u4,$v4" $outfile
+else
+ convert $tmpA -virtual-pixel $vp -background $bgcolor \
+ -mattecolor $skycolor $distort Perspective \
+ "0,0 $u1,$v1 $maxwidth,0 $u2,$v2 $maxwidth,$maxheight $u3,$v3 0,$maxheight $u4,$v4" $outfile
+fi
+exit 0
diff --git a/bevelborder b/bevelborder
new file mode 100755
index 0000000..6f6ec2a
--- /dev/null
+++ b/bevelborder
@@ -0,0 +1,295 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 7/16/2010 .......... revised 6/30/2011
+#
+# USAGE: bevelborder [-s size] [-m method] [-p percent] [-c contrast] [-b bcolor] [-a amount] [-t type] infile outfile
+# USAGE: bevelborder [-h or -help]
+#
+# OPTIONS:
+#
+# -s size size of border in pixels; same in both dimensions;
+# default equals 10% of min(imagewidth, imageheight)
+# -m method bevel method; choices are: outer, inner or split;
+# default=outer
+# -p percent split percent between outer and inner bevel;
+# 100 is outer bevel only; 0 is inner bevel only;
+# default=50 applies only to method=split
+# -c contrast contrast percent for bevel; 0<=integer<=100;
+# default=50
+# -b bcolor border coloring; any IM opaque color is allowed;
+# default is no coloring
+# -a amount amount of border coloring; 0<=integer<=100;
+# default=25
+# -t type type of compose; hardlight, linearlight or
+# vividlight; default=hardlight
+#
+###
+#
+# NAME: BEVELBORDER
+#
+# PURPOSE: To applies a bevel effect to the border of an image.
+#
+# DESCRIPTION: BEVELBORDER applies a bevel effect to the border of an image.
+# The bevel can be an outer bevel (raised effect), an inner bevel (depressed
+# effect) or a split (mix) of the two. The border may also be colorized.
+#
+#
+# ARGUMENTS:
+#
+# -s size ... SIZE is the dimensions of the border region in pixels. The same
+# value is used in both dimensions. Values are integers greater than 0. The
+# default is 10% of the min(width,height) of the image.
+#
+# -m method ... METHOD is the bevel method. Choices are: outer, which makes a
+# raised effect; inner, which makes a depressed effect; and split, which is
+# part raised and part depressed. The amount of each is controlled by the
+# percent argument. The default is outer.
+#
+# -p percent ... PERCENT is the percent split between outer and inner bevels.
+# Values are integers such that 0<=percent<=100. A value of 100 is full outer
+# bevel. A value of 0 is full inner bevel. The default is 50 and only applies
+# when the method=split.
+#
+# -c contrast ... CONTRAST percent for innerbevel or outerbevel. Values are
+# integers between 0 and 100. The default=50.
+#
+# -b bcolor ... BCOLOR is the bevel colorization color. Any valid opaque IM
+# color is allowed. The default is no additional colorization.
+#
+# -a amount ... AMOUNT of colorization. Values are integers between 0 and 100.
+# The default=25.
+#
+# -t type ... TYPE of compose. Choices are: hardlight, linearlight and
+# vividlight. The default=hardlight
+#
+# REQUIREMENTS: IM 6.5.9.0 or higher due to the use of -brightness-contrast.
+#
+# 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
+size="" # bevel border amount in pixels
+method="outer" # outer, inner, split
+percent=50 # split percent; 0 to 100; 100 is outer; 0 is inner
+contrast=50 # bevel contrast percent
+bcolor="" # bevel color
+amount=25 # amount of coloring; 0<=integer<=100
+type="hardlight" # bevel compose method
+
+# 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 -n '/^###/q; /^#/!q; s/^#//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME"
+ }
+usage2()
+ {
+ echo >&2 ""
+ echo >&2 "$PROGNAME:" "$@"
+ sed >&2 -n '/^######/q; /^#/!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
+ ;;
+ -s) # get size
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID SIZE SPECIFICATION ---"
+ checkMinus "$1"
+ size=`expr "$1" : '\([0-9]*\)'`
+ [ "$size" = "" ] && errMsg "--- SIZE=$size MUST BE A NON-NEGATIVE INTEGER VALUE (with no sign) ---"
+ testA=`echo "$size <= 0" | bc`
+ [ $testA -eq 1 ] && errMsg "--- SIZE=$size MUST BE A POSITIVE INTEGER ---"
+ ;;
+ -m) # get method
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID METHOD SPECIFICATION ---"
+ checkMinus "$1"
+ # test type values
+ method=`echo "$1" | tr "[:upper:]" "[:lower:]"`
+ case "$method" in
+ outer|inner|split) ;; # do nothing - valid type
+ *) errMsg "--- METHOD=$method IS NOT A VALID VALUE ---" ;;
+ esac
+ ;;
+ -p) # get percent
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID PERCENT SPECIFICATION ---"
+ checkMinus "$1"
+ percent=`expr "$1" : '\([0-9]*\)'`
+ [ "$percent" = "" ] && errMsg "--- PERCENT=$percent MUST BE A NON-NEGATIVE INTEGER ---"
+ testA=`echo "$percent < 0" | bc`
+ testB=`echo "$percent > 100" | bc`
+ [ $testA -eq 1 -o $testB -eq 1 ] && errMsg "--- PERCENT=$percent MUST BE AN INTEGER BETWEEN 0 AND 100 ---"
+ ;;
+ -c) # get contrast
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID CONTRAST SPECIFICATION ---"
+ checkMinus "$1"
+ contrast=`expr "$1" : '\([0-9]*\)'`
+ [ "$contrast" = "" ] && errMsg "--- CONTRAST=$contrast MUST BE A NON-NEGATIVE INTEGER ---"
+ testA=`echo "$contrast < 0" | bc`
+ testB=`echo "$contrast > 100" | bc`
+ [ $testA -eq 1 -o $testB -eq 1 ] && errMsg "--- CONTRAST=$contrast MUST BE AN INTEGER BETWEEN 0 AND 100 ---"
+ ;;
+ -b) # get bcolor
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID BCOLOR SPECIFICATION ---"
+ checkMinus "$1"
+ bcolor="$1"
+ ;;
+ -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 A NON-NEGATIVE INTEGER ---"
+ testA=`echo "$amount < 0" | bc`
+ testB=`echo "$amount > 100" | bc`
+ [ $testA -eq 1 -o $testB -eq 1 ] && errMsg "--- AMOUNT=$amount MUST BE AN INTEGER BETWEEN 0 AND 100 ---"
+ ;;
+ -t) # get type
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID TYPE SPECIFICATION ---"
+ checkMinus "$1"
+ # test type values
+ type=`echo "$1" | tr "[:upper:]" "[:lower:]"`
+ case "$type" in
+ hardlight|linearlight|vividlight) ;; # do nothing - valid type
+ *) errMsg "--- METHOD=$method IS NOT A VALID VALUE ---" ;;
+ esac
+ ;;
+ -) # 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"
+
+
+tmpA="$dir/bevelborder_$$.mpc"
+tmpB="$dir/bevelborder_$$.cache"
+trap "rm -f $tmpA $tmpB; exit 0" 0
+trap "rm -f $tmpA $tmpB; exit 1" 1 2 3 15
+
+
+# read the input image into the TMP cached image.
+convert -quiet -regard-warnings "$infile" +repage "$tmpA" ||
+ errMsg "--- FILE $infile NOT READABLE OR HAS ZERO SIZE ---"
+
+# set default size
+if [ "$size" = "" ]; then
+ size=`convert $tmpA -ping -format "%[fx:floor(0.1*min(w,h))]" info:`
+ wsize=$size
+ hsize=$size
+fi
+
+# get input image size
+ww=`convert $infile -ping -format "%w" info:`
+hh=`convert $infile -ping -format "%h" info:`
+wd=`convert xc: -format "%[fx:$ww-2*$size]" info:`
+ht=`convert xc: -format "%[fx:$hh-2*$size]" info:`
+
+# adjust contrast and transparency to fractions
+contr1=`convert xc: -format "%[fx:$contrast-100]" info:`
+
+# setup bevel parameters
+if [ "$method" = "outer" ]; then
+ wsize2=$size
+ hsize2=0
+elif [ "$method" = "inner" ]; then
+ hsize2=$size
+ wsize2=0
+elif [ "$method" = "split" ]; then
+ wsize2=`convert xc: -format "%[fx:floor($percent*$size/100)]" info:`
+ hsize2=$(($size-$wsize2))
+fi
+# echo "ww=$ww; hh=$hh; wd=$wd; ht=$ht; wsize2=$wsize2; hsize2=$hsize2"
+
+if [ "$bcolor" = "" ]; then
+ colorize=""
+else
+ colorize="-fill $bcolor -colorize $amount%"
+fi
+
+# process image
+convert $tmpA -size ${wd}x${ht} xc:"gray(50%)" \
+\( -clone 1 -frame ${size}x${size}+${wsize2}+${hsize2} \
+-auto-level -black-threshold 25% -white-threshold 75% $colorize \
+-brightness-contrast 0,${contr1}% \
+-clone 1 -gravity center -composite \) \
+-delete 1 -compose $type -composite \
+$outfile
+
+exit 0
diff --git a/breaker.py b/breaker.py
new file mode 100755
index 0000000..99e711b
--- /dev/null
+++ b/breaker.py
@@ -0,0 +1,394 @@
+#!/usr/bin/python2.7
+from subprocess import call, Popen, PIPE
+import urllib
+import urllib2
+import os
+import sys
+import random
+import re
+import time
+urlencode = urllib.urlencode
+urlopen = urllib2.urlopen
+Request = urllib2.Request
+
+WORKING_DIR = "/tmp"
+BIN_CONVERT = "/usr/bin/convert"
+BIN_IDENTIFY = "/usr/bin/identify"
+DEFAULT_FINALFORMAT = "png";
+
+SUBTLE_BREAK_MARK = 'pron'
+EXTREME_BREAK_MARK = 'sugar'
+
+HEADER_OFFSET = 5000
+
+MAX_SIZE = 1024 * 1024 * 1.2 * 1.5
+
+# 'CLASSIC':'jpg',
+# 'REDUX':'pcds',
+# 'BLURRY_BREAK':'viff',
+# 'BLURRY_BREAK_2':'mat',
+# 'SWIPE':'miff',
+# 'RGB_WASH':'psd',
+# 'RGB_WASH_2':'psb',
+# 'NOISY_BREAK':'palm',
+# 'NOISY_BREAK_2':'fig',
+# 'BROKEN_VIGNETTE':'pbm',
+# 'FAX_MACHINE':'cals',
+# 'STRIPES':'exr',
+# 'PHOTOCOPY':'art',
+TEST_PARAMS = {
+ "url" : "http://i.asdf.us/im/27/1424816234661dumpfmpfifferkinggr_1424816412_pfifferking.gif" ,
+ "breaktype" : "RGB_WASH",
+ "finalformat" : "png",
+ "breakmode" : "extreme",
+ "breakangle" : "10",
+ "username" : "donkey",
+ "expanded" : "false"
+}
+
+def bool_correct(s):
+ if re.match(r'^false$', s, re.IGNORECASE):
+ return False
+ elif re.match(r'^true$', s, re.IGNORECASE):
+ return True
+ else:
+ return s
+
+#{{{Utility functions
+class dotdict(dict):
+ """dot.notation access to dictionary attributes"""
+ def __getattr__(self, attr):
+ return self.get(attr)
+ __setattr__= dict.__setitem__
+ __delattr__= dict.__delitem__
+def sanitize (str):
+ return re.sub(r'\W+', '', str)
+
+def now():
+ return int(time.time())
+
+def browser_request (url, data=None):
+ headers = {
+ 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
+ 'Accept': '*/*',
+ }
+ try:
+ req = Request(url, data, headers)
+ response = urlopen(req)
+ except IOError, e:
+ if hasattr(e, 'code'):
+ sys.stderr.write( '%s - ERROR %s' % (url, e.code) )
+ raise;
+ return None
+ else:
+ return response
+
+def download(url, destination, max_size=MAX_SIZE):
+ response = browser_request(url, None)
+ rawimg = response.read()
+ if len(rawimg) == 0:
+ sys.stderr.write("got zero-length file")
+ raise;
+ if len(rawimg) > max_size:
+ sys.stderr.write("file too big: max size {} KB / {} is {} KB".format(
+ str(MAX_SIZE/1024),
+ destination,
+ str(len(rawimg)/1024)
+ ))
+ raise;
+ f = open(destination, "w")
+ f.write(rawimg)
+ f.close()
+
+def dimensions (filepath):
+ #works in lieu of a mimetype check (it reads the header as well)
+ ident = (Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0]).split(" ")
+ return ident[2].split("x")
+
+def file_size (filepath):
+ try:
+ return os.stat(file)[6]
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+def gif_frames(filepath):
+ try:
+ info = Popen([BIN_IDENTIFY,filepath], stdout=PIPE).communicate()[0]
+ frames = filter((lambda x: x), map(
+ (lambda x: x.split(" ")[0]),
+ (info).split('\n')
+ ))
+ return frames
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+#}}}
+
+class Breaker():
+ def __init__(self, params):
+ self.params = {}
+ self.now = now()
+ self.tag = "imBreak"
+ self.commands = [];
+ self.required_keys = [
+ "url",
+ "breaktype",
+ "finalformat",
+ "breakmode",
+ "breakangle",
+ "username",
+ "expanded"
+ ]
+ self.files_created = []
+ for k in self.required_keys:
+ if k in params:
+ if k == 'breaktype':
+ self.params['breaktype'] = self._get_breaktype(params[k])
+ elif k == 'url':
+ self.params[k] = params[k]
+ else:
+ self.params[k] = bool_correct(sanitize(params[k]))
+ else:
+ self.params[k] = False;
+
+
+ self.params = dotdict(self.params)
+
+ self.basename, self.first_format = self._get_filename();
+ self.downloaded_file = os.path.join(WORKING_DIR, "IMBREAKTMP{}.{}".format(self.basename, self.first_format))
+
+ try:
+ download(self.params.url, self.downloaded_file)
+ self.files_created.append(self.downloaded_file)
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+ self.gif_frames = gif_frames(self.downloaded_file)
+ self.gif_frames = self.gif_frames if len(self.gif_frames) > 1 else False
+ self.width, self.height = dimensions(self.downloaded_file)
+
+ if not self.params.finalformat:
+ self.params.finalformat = DEFAULT_FINALFORMAT
+ if self.gif_frames:
+ self.params.finalformat = 'gif'
+ if self.params.breaktype == 'miff':
+ self.params.finalformat = 'jpg'
+ self.params.breakmode = 'subtle'
+ #final filepath is stored in self.filepath
+ self.filename = "{}.{}".format(self.basename, self.params.finalformat)
+ self.filepath = os.path.join(WORKING_DIR, self.filename)
+ self.conversion_file = os.path.join(WORKING_DIR, "IMBREAKTMP{}.{}".format(self.basename, self.params.breaktype))
+
+ def _get_breaktype(self, key):
+ #{{{ conversion table
+ breaktypeTranslate = {
+ 'CLASSIC':'jpg',
+ 'REDUX':'pcds',
+ 'BLURRY_BREAK':'viff',
+ 'BLURRY_BREAK_2':'mat',
+ 'SWIPE':'miff',
+ 'RGB_WASH':'psd',
+ 'RGB_WASH_2':'psb',
+ 'NOISY_BREAK':'palm',
+ 'NOISY_BREAK_2':'fig',
+ 'BROKEN_VIGNETTE':'pbm',
+ 'FAX_MACHINE':'cals',
+ 'STRIPES':'exr',
+ 'PHOTOCOPY':'art',
+ }
+ #}}}
+ return breaktypeTranslate[key]
+
+ def _get_filename (self):
+ url = self.params.url
+ name_part = "";
+ file_format = "";
+ if "?" in url:
+ url = url.split("?")[0]
+ if "/" in url:
+ url = urllib.unquote(url).replace(" ","")
+ name_part = url.split("/")[-1]
+ try:
+ parts = name_part.split(".")
+ name_part = sanitize(parts[-2])
+ file_format = sanitize(parts[-1])
+ if not name_part or not file_format:
+ sys.stderr.write( "Incompatible input file type")
+ raise;
+ except Exception as e:
+ sys.stderr.write( "Incompatible input file type")
+ raise;
+ else:
+ sys.stderr.write( "Incompatible url")
+ raise;
+ if (len(name_part) > 20):
+ name_part = name_part[:-20]
+ return "{}{}_{}_{}".format(self.tag, name_part, self.now, self.params.username or ""), file_format
+
+#{{{#########rotatefunctions#######################################
+ def _rotate(self):
+ try:
+ call([BIN_CONVERT,self.downloaded_file,"-rotate",self.params.breakangle,"+repage",self.downloaded_file])
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+ def _rotate_back(self):
+ try:
+ angle = str(360-int(self.params.breakangle))
+ call([BIN_CONVERT,self.filepath,"-rotate",angle,"+repage",self.filepath])
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+ if not self.params.expanded:
+ try:
+ call(
+ [BIN_CONVERT,self.filepath,"-gravity","Center","-crop","{}x{}+0+0".format(
+ self.width, self.height),"+repage",self.filepath
+ ])
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+#}}}
+ def _subtle_break(self):
+ #assume the header is no longer than HEADER_OFFSET bytes
+ breakpoint = random.randint(HEADER_OFFSET, len(self.file_data))
+ newfile = ""
+ newfile = self.file_data[0:breakpoint];
+ newfile += SUBTLE_BREAK_MARK;
+ newfile += self.file_data[breakpoint:]
+ self.file_data = newfile[0:len(self.file_data)]
+
+ def _extreme_break(self):
+ increment = len(self.file_data)/10;
+ i = 0
+ newfile = "";
+ for b in self.file_data:
+ if i > HEADER_OFFSET and not (i % increment):
+ b += EXTREME_BREAK_MARK
+ newfile += b
+ i += 1
+ self.file_data = newfile[0:len(self.file_data)]
+
+ def _choose_frame(self):
+ frame = random.choice(self.gif_frames)
+ try:
+ call([BIN_CONVERT, frame, self.downloaded_file])
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+ def _enforce_jpg(self):
+ if self.params.breaktype in [ "exr", "bmp", "miff" ] and not re.match(r'jpe?g', self.first_format, re.IGNORECASE):
+ jpg_file = os.path.join(WORKING_DIR, "{}.{}".format(self.basename, "jpg"))
+ try:
+ call([BIN_CONVERT,self.downloaded_file,jpg_file])
+ call(["rm",self.downloaded_file])
+ self.downloaded_file = jpg_file
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+ def _first_conversion(self):
+ if self.first_format == self.params.breaktype:
+ self.downloaded_file = self.conversion_file
+ return
+ try:
+ call([BIN_CONVERT, self.downloaded_file, self.conversion_file]);
+ self.files_created.append(self.conversion_file)
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+ def _read_data(self, filepath):
+ f = open(filepath, 'r');
+ data = f.read()
+ f.close()
+ return data
+
+ def _prepare_filedata(self):
+ if self.gif_frames:
+ self._choose_frame()
+ if self.params.breakangle:
+ self._rotate()
+ self._enforce_jpg();
+ self._first_conversion();
+ self.file_data = self._read_data(self.conversion_file)
+ if not self.file_data:
+ sys.stderr.write("Unable to get file_data")
+ raise;
+
+ def _add_false_data(self, breakmode):
+ if breakmode == "subtle":
+ self._subtle_break()
+ elif breakmode == "extreme":
+ self._extreme_break()
+ f = open(self.conversion_file, 'w')
+ f.write(self.file_data)
+ f.close();
+
+#{{{ SHRINK (UNUSED)
+ def _shrink(self):
+ cmd = [ BIN_CONVERT, "-resize", "500x500", self.downloaded_file, self.downloaded_file ];
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd));
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+#}}}
+
+ def _final_conversion(self):
+ cmd = [BIN_CONVERT, self.conversion_file, self.filepath]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd));
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+ #handle multiple files created by psd and psb
+ def psd_psbfilepath(num):
+ return os.path.join(WORKING_DIR, "{}-{}.{}".format(self.basename, num, self.params.finalformat))
+ if self.params.breaktype == 'psd':
+ cmd = ['mv', psd_psbfilepath(1), self.filepath]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd));
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+ self.files_created.append(psd_psbfilepath(0))
+ if self.params.breaktype == 'psb':
+ cmd = ['mv', psd_psbfilepath(0), self.filepath]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd));
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+ self.files_created.append(psd_psbfilepath(1))
+
+ if self.params.breakangle:
+ self._rotate_back()
+
+ def _cleanup(self):
+ cmd = ["rm"]+self.files_created
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd));
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+
+ def create(self, breakmode=""):
+ if not breakmode: breakmode = self.params.breakmode
+ self._prepare_filedata();
+ self._add_false_data(breakmode);
+ self._final_conversion()
+ self._cleanup()
+
+
+if __name__ == "__main__":
+ b = Breaker(TEST_PARAMS)
+ b.create();
+ print b.filepath
diff --git a/db.py b/db.py
new file mode 100755
index 0000000..23ebbe4
--- /dev/null
+++ b/db.py
@@ -0,0 +1,31 @@
+import MySQLdb
+USER = "asdfus"
+PASSWORD = "urJJwLjdO9GPuOxk"
+DATABASE = "asdfus"
+
+class db:
+ def __init__ (self):
+ self.conn = None
+ self.cursor = None
+ self.connect()
+
+ def connect (self):
+ self.conn = MySQLdb.connect (host = "localhost",
+ user = USER,
+ passwd = PASSWORD,
+ db = DATABASE
+ )
+ self.cursor = self.conn.cursor ()
+
+ def execute (self,sql,args=()):
+ try:
+ self.cursor.execute(sql,args)
+ except MySQLdb.Error, e:
+ print "Error %d: %s" % (e.args[0], e.args[1])
+ # sys.exit (1)
+ self.connect()
+ self.cursor.execute(sql,args)
+
+ def lastinsertid (self):
+ return DB.conn.insert_id()
+
diff --git a/gradient.py b/gradient.py
new file mode 100755
index 0000000..fbdba3f
--- /dev/null
+++ b/gradient.py
@@ -0,0 +1,257 @@
+#!/usr/bin/python2.7
+import re
+import time
+from subprocess import call
+import simplejson as json
+import sys
+import os
+import db
+import sha
+#{{{ logging
+#import logging
+#logger = logging.getLogger('gradient')
+#logging.FileHandler('/var/www/cache/gradient.log')
+#formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
+#hdlr.setFormatter(formatter)
+#logger.addHandler(hdlr)
+#logger.setLevel(logging.WARNING)
+#}}}
+
+BASE_URL = "http://localhost:8080/"
+PARAM_LIST = [
+ "width", "height",
+ "color1", "color2",
+ "stripes",
+ "stripenumber", "stripeintensity",
+ "blurriness",
+ "contrast",
+ "brightness", "saturation", "hue",
+ "halftone",
+ "bevel", "percentbeveled",
+ "rotate", "flip", "flop", "tilt",
+ "filetype",
+ "gradienttype",
+ "username",
+]
+BIN_CONVERT = "/usr/bin/convert"
+BIN_IDENTIFY = "/usr/bin/identify"
+BEVELBORDER = "./bevelborder"
+WEB_ADDR = "http://i.asdf.us"
+DEFAULT_FORMAT = "png"
+DEFAULT_COLORS = {
+ "color1" : "white",
+ "color2" : "black",
+};
+
+WORKING_DIR = '/tmp'
+DEFAULT_WIDTH = "200"
+DEFAULT_HEIGHT = "200"
+
+REMOTE_ADDRESS = ""
+
+BEVELVALUES = {
+ "flatout": ["-s","-m","outer"],
+ "flatinner": ["-s","-m","inner"],
+ "evenlyframed": ["-s ", "-m ", "split"],
+ "biginner": ["-s","-m","outer","-c","50","-b","red","-a","25"],
+ "bigouter": ["-s","-m","split","-c","50","-b","red","-a","25"],
+ "dramaticflatout": ["-s","-m","outer","-a","25","-b","blue"],
+ "dramaticflatinner": ["-s","-m","outer","-a","25","-b","blue"],
+ }
+BEVEL_DEFAULT = "12";
+
+HALFTONEVALUES = {
+ "checkeredfade": "h6x6a",
+ "etchedtransition": "o8x8",
+ "bendaydots": "h16x16o",
+ "smallerdots1": "h8x8o",
+ "smallerdots2": "c7x7w",
+ "flatstripes": "o2x2",
+ }
+
+def sanitize (s):
+ return re.sub(re.compile(r'\W+'), '', s)
+
+def is_number(s):
+ try:
+ return int(s)
+ except (ValueError, TypeError):
+ return False
+
+class Gradient:
+ def __init__(self, form):
+ self.now = int(time.time())
+ self.tag = "imGradient"
+ self.commands = []
+ self.filename = ""
+ self.filepath = ""
+
+ params = {}
+ for key in PARAM_LIST:
+ if key in form:
+ if key in ['color1', 'color2']:
+ params[key] = form[key]
+ #params[key] = form[key].value
+ else:
+ params[key] = sanitize(form[key])
+ #params[key] = sanitize(form[key].value)
+
+ if key in ['rotate','tilt','blurriness','stripenumber','stripeintensity']:
+ params[key] = params[key] if is_number(params[key]) else ""
+ elif key in ['brightness', 'contrast', 'hue']:
+ if not is_number(params[key]) or params[key] == "100": params[key] = ""
+ else:
+ params[key] = ""
+ params['width'] = params['width'] if is_number(params['width']) else DEFAULT_WIDTH
+ params['height'] = params['height'] if is_number(params['height']) else DEFAULT_HEIGHT
+ params["color1"] = params["color1"] or DEFAULT_COLORS["color1"];
+ params["color2"] = params["color2"] or DEFAULT_COLORS["color2"];
+ self.params = params
+
+ def newfilename(self):
+ return "{}{}-{}_{}_{}.{}".format(
+ self.tag,
+ self.params['color1'].replace('#',''),
+ self.params['color2'].replace('#',''),
+ self.now,
+ self.params['username'],
+ self.params['filetype'] or DEFAULT_FORMAT,
+ )
+
+ def buildCmd(self, filename, directory=WORKING_DIR):
+ cmd = [BIN_CONVERT]
+ cmd.extend([
+ '-size',
+ "{}x{}".format(self.params["width"],self.params["height"])
+ ])
+ if self.params['rotate']: cmd.extend(["-rotate", self.params["rotate"]])
+ if self.params['tilt']: cmd.extend(["-distort","SRT",self.params['tilt']])
+ if self.params['flip'] == "true": cmd.append("-flip")
+ if self.params['flop'] == "true": cmd.append("-flop")
+ if self.params['contrast']: cmd.extend(["-contrast-stretch", self.params['contrast']])
+ gradients = {
+ "canvas" : ["canvas:{}".format(self.params['color1'])],
+ "radial" : [
+ "radial-gradient:{}-{}".format( self.params['color1'], self.params['color2'])
+ ],
+ "colorspace" : [
+ "-colorspace",
+ "Gray",
+ "plasma:{}-{}".format(self.params['color1'], self.params['color2'])
+ ],
+ "mirrored" : [
+ "plasma:{}-{}".format(self.params['color1'], self.params['color2']),
+ "\(","+clone","-flop","\)",
+ "append"
+ ],
+ "plasmawash" : [
+ "plasma:{}-{}".format(self.params['color1'], self.params['color2']),
+ "-set","colorspace","HSB"
+ ],
+ "gradientwash" : [
+ "gradient:{}-{}".format(self.params['color1'], self.params['color2']),
+ "-set","colorspace","HSB"
+ ],
+ "noise" : ["xc:","+noise","Random","-virtual-pixel","tile"]
+ }
+ if self.params["gradienttype"] in gradients:
+ cmd.extend(gradients[self.params['gradienttype']])
+ else:
+ cmd.append("gradient:{}-{}".format(self.params['color1'], self.params['color2']))
+
+ if self.params['blurriness']:
+ cmd.extend(["-blur","0x{}".format(self.params["blurriness"]),"-auto-level"])
+
+ if self.params['stripes'] == "true" and len(self.params['stripenumber']):
+ cmd.extend(["-function","Sinusoid"])
+ if self.params['stripeintensity']:
+ cmd.append("{},{}".format(self.params['stripenumber'],self.params["stripeintensity"]))
+ else:
+ cmd.append(self.params['stripenumber'])
+ if self.params["halftone"] in HALFTONEVALUES:
+ cmd.extend([
+ "-ordered-dither",
+ HALFTONEVALUES[self.params["halftone"]]
+ ])
+ cmd += [
+ '-modulate',
+ "{},{},{}".format(
+ self.params['brightness'] or "100",
+ self.params['saturation'] or "100",
+ self.params['hue'] or "100")
+ ]
+ cmd.append(os.path.join(directory,filename));
+# logger.warning('in buildCmd: {}' % cmd)
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd));
+ except Exception as e:
+ sys.stderr.write( "ERROR:{}".format(e))
+ raise;
+ return cmd
+
+
+ def makeBevel(self, filename, directory):
+ def get_bevelvalue(key, bevpercentval):
+ bevel_args = BEVELVALUES[key]
+ bevel_args.insert(1, bevpercentval)
+ return bevel_args
+ if self.params['percentbeveled']:
+ try:
+ w, h = map(int, (self.params['width'], self.params['height']))
+ except Exception as e:
+ sys.stderr.write( "ERROR: {}".format(e))
+ exit(1);
+ if h >= w:
+ bevpercentval = str(int(self.params['percentbeveled'])*0.005*int(h))
+ else:
+ bevpercentval = str(int(self.params['percentbeveled'])*0.005*int(w))
+ else:
+ bevpercentval = BEVEL_DEFAULT
+
+ bevel = get_bevelvalue(self.params['bevel'], bevpercentval)
+ cmd = [BEVELBORDER]
+ cmd += bevel
+ cmd += [ os.path.join(directory,filename), os.path.join(directory, filename) ]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write( "ERROR: {}".format(e))
+ raise;
+
+ def create(self, directory=WORKING_DIR):
+ self.filename = self.newfilename()
+ self.filepath = os.path.join(directory, self.filename)
+ self.buildCmd(self.filename, directory)
+ if self.params['bevel'] in BEVELVALUES:
+ self.makeBevel(self.filename, directory)
+ self.output = [
+ str(os.stat(self.filepath)[6]),
+ "{}px".format(self.params["width"]),
+ "{}px".format(self.params["height"]),
+ ]
+
+TEST_FORM = {
+ "width" : "200",
+ "color1" : "#ffdead",
+ "color2" : "blue",
+ "stripes" : "true",
+ "stripenumber" : "20",
+ "gradienttype" : "radial",
+ "stripeintensity" : "20",
+ "halftone" : "checkeredfade",
+ "percentbeveled" : "30",
+ "flip" : "true",
+ "bevel" : "flatout",
+ "rotate" : "20",
+ "height" : "200",
+ "filetype" : "jpg",
+ "username" : "whatever"
+}
+if __name__ == "__main__":
+ g = Gradient(TEST_FORM);
+ g.create();
+ print g.now
+ print " ".join(g.commands)
+ print "\n".join(g.output)
diff --git a/grid b/grid
new file mode 100755
index 0000000..58d0e08
--- /dev/null
+++ b/grid
@@ -0,0 +1,241 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/22/2007 .......... revised 1/1/2008
+#
+# USAGE: grid [-s spacing] [-c color] [-t thickness] [-o opacity] infile outfile
+# USAGE: grid [-h or -help]
+#
+# OPTIONS:
+#
+# -s spacing x,y spacing between grid lines; default=16,16 or 16;
+# second number is defaulted to the first
+# -c color color of grid lines; default="black"
+# -t thickness thickness of grid lines; default=1
+# -o opacity opacity of grid lines opacity between 0.0 and 1.0;
+# opacity=0 is transparent; opacity=1 is opaque;
+# default=1
+#
+###
+#
+# NAME: GRID
+#
+# PURPOSE: To superimpose a set of horizontal and/or vertical grid lines
+# on an image.
+#
+# DESCRIPTION: GRID superimposes a set of horizontal and/or vertical grid
+# lines on an image. Parameters are available to select the grid line color,
+# thickness and opacity.
+#
+#
+# OPTIONS:
+#
+# -s spacing ... SPACING specifies the horizontal (x) and vertical (y) grid
+# offset between lines. Spacing must be provided as integer values. If the
+# second value is left off, then it will be set identical to the first. If
+# only vertical lines are desired, set the horizontal (x) spacing larger
+# than the width of the image. If only horizontal lines are desired, set
+# the vertical (y) spacing larger than the height of the image. The default=8.
+# Note: if you want the bottom and/or right grid line to show, then the image
+# dimension(s) must be a multiple of the grid spacing plus 1.
+#
+# -c color ... COLOR is the color of the grid lines. Any valid IM color
+# specification is allowed. Be sure to color values in double quotes.
+# The default="black".
+#
+# -t thickness ... THICKNESS is the grid line thickness. Values are positive
+# integers. The default=1
+#
+# -o opacity ... OPACITY is the grid line opacity. Values are non-negative
+# floats between 0.0 and 1.0. The default=1
+#
+# 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
+spacing=16
+color="black"
+thickness=1
+opacity=1
+
+
+# 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 -n '/^###/q; /^#/!q; s/^#//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME"
+ }
+usage2()
+ {
+ echo >&2 ""
+ echo >&2 "$PROGNAME:" "$@"
+ sed >&2 -n '/^######/q; /^#/!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 10 ]
+ 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
+ ;;
+ -s) # get spacing
+ shift # to get the next parameter - spacing
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID SPACING SPECIFICATION ---"
+ checkMinus "$1"
+ spacing="$1,"
+ ;;
+ -c) # get color
+ shift # to get the next parameter - lineval
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID COLOR SPECIFICATION ---"
+ checkMinus "$1"
+ # test lineval values
+ color="$1"
+ ;;
+ -t) # get thickness
+ shift # to get the next parameter - thickness
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID THICKNESS SPECIFICATION ---"
+ checkMinus "$1"
+ # test width values
+ thickness=`expr "$1" : '\([0-9]*\)'`
+ [ "$thickness" = "" -o $thickness -eq 0 ] && errMsg "--- THICKNESS=$thickness MUST BE A POSITIVE INTEGER ---"
+ ;;
+ -o) # get opacity
+ shift # to get the next parameter - opacity
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID OPACITY SPECIFICATION ---"
+ checkMinus "$1"
+ # test width values
+ opacity=`expr "$1" : '\([.0-9]*\)'`
+ [ "$opacity" = "" ] && errMsg "OPACITY=$opacity IS NOT A NON-NEGATIVE FLOATING POINT NUMBER"
+ opacitytest=`echo "$opacity > 1" | bc`
+ [ $opacitytest -eq 1 ] && errMsg "OPACITY=$opacity MUST BE BETWEEN 0.0 AND 1.0"
+ ;;
+ -) # 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 temporary images and auto delete upon exit
+# use mpc/cache to hold input image temporarily in memory
+tmpA="$dir/profile_$$.mpc"
+tmpB="$dir/profile_$$.cache"
+trap "rm -f $tmpA $tmpB; exit 0" 0
+trap "rm -f $tmpA $tmpB; exit 1" 1 2 3 15
+
+
+#
+if convert -quiet -regard-warnings "$infile" +repage "$tmpA"
+ then
+ : 'do nothing - continue processing below'
+ else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---"
+fi
+
+
+# get image dimensions
+width=`identify -format %w $tmpA`
+height=`identify -format %h $tmpA`
+
+width1=`expr $width - 1`
+height1=`expr $height - 1`
+
+xinc=`echo "$spacing" | cut -d, -f1`
+yinc=`echo "$spacing" | cut -d, -f2`
+[ "$yinc" = "" ] && yinc=$xinc
+testx=`expr $xinc : '[0-9]*'`
+testy=`expr $yinc : '[0-9]*'`
+[ $testx -eq 0 -o $testy -eq 0 ] && errMsg "--- SPACING MUST BE AN INTEGER ---"
+
+# get string for drawing grid lines
+drawstr=""
+
+if [ $yinc -le $height ]
+ then
+ i=0
+ while [ $i -le $height ]
+ do
+ drawstr="$drawstr M 0,$i L $width1,$i"
+ i=`expr $i + $yinc`
+ done
+fi
+
+if [ $xinc -le $width ]
+ then
+ i=0
+ while [ $i -le $width ]
+ do
+ drawstr="$drawstr M $i,0 L $i,$height1"
+ i=`expr $i + $xinc`
+ done
+fi
+
+# process image
+convert $tmpA -fill none -stroke $color -strokewidth $thickness -draw "stroke-opacity $opacity path '$drawstr'" $outfile
+exit 0
diff --git a/imbreak_main.js b/imbreak_main.js
new file mode 100644
index 0000000..78ae753
--- /dev/null
+++ b/imbreak_main.js
@@ -0,0 +1,113 @@
+var Main =
+ {
+ firsttime: true,
+ generating: false,
+ thelast: "",
+ enter: function (e)
+ {
+ if (Main.generating)
+ return
+ if (e.keyCode === 13)
+ Main.go()
+ },
+ go: function ()
+ {
+ if (Main.generating)
+ return
+ Main.generating = true
+ var theloader = '<span style="width:100%;margin-right:40%"><img style="width:140px;height:120px;display:inline;" src="generating.gif"></img></span>'
+ $("#output-cmd").html(theloader).show()
+ $('.result').show()
+ $('.results').show()
+ $("#output-img").show()
+ $("#output-url").show()
+ $("#result").show()
+ var data =
+ {
+ breakmode:$('input:radio[name=modeswitch]:checked').val(),
+ breaktype: $('#breaktype :selected').val(),
+ breakangle: $("#breakangle").val(),
+ url: $('#url').val(),
+ username: $('#username').val(),
+ firsttime: Main.firsttime.toString()
+ }
+ if (data["breakmode"] == "gradual")
+ {
+ data["breakmode"] = "subtle"
+ if (Main.lines && Main.thelast == $('#url').val())
+ {
+ Main.firsttime = false
+ data["url"] = Main.lines[1]
+ }
+ }
+ else
+ {
+ Main.firsttime = true
+ }
+ Main.thelast = $('#url').val();
+ thestring = JSON.stringify(data);
+ $('#error').append(thestring);
+ if (data.username.length > 0)
+ document.cookie = "imname="+data.username+";path=/;domain=.asdf.us;max-age=1086400"
+ $.post("/cgi-bin/im/break/breaker", data, Main.callback)
+ },
+ error: function (s)
+ {
+ $("#output-cmd").html("<span class='error'>ERROR: " + s + "</span>").show()
+ $("#output-url").hide()
+ $("#output-img").hide()
+ },
+ filesize: function (size)
+ {
+ if (size < 1024)
+ return size.toString() + " bytes"
+ if (size < 1024 * 1024)
+ return Math.floor (size/1024).toString() + " KB"
+ else
+ return Math.floor (size/(1024*1024)).toString() + " MB"
+ },
+ callback: function (data)
+ {
+ data = JSON.parse(data)
+ $('#error').append('called');
+ $("#output-cmd").html('')
+ $('#output-url').val(data.url)
+ $("#output-img").html("click image to enlarge<br><a target=_blank href='"+data.url+"'>"+"<img src='"+data.url+"' id='output-image'></img><br>"+"</a>"
+);
+ $("#output-info").html('-ACTUAL SIZE-<br>'+Main.filesize(data.size)+'<br>'+data.width+'<br>'+data.height+'<br><br>'+'<span>see more at &rarr;<a href="http://asdf.us/im/gallery">photoblaster gallery</a></span>'+'<br>')
+ Main.generating = false
+ },
+ cookie: function ()
+ {
+ if (document.cookie)
+ {
+ var cookies = document.cookie.split(";")
+ for (i in cookies)
+ {
+ var cookie = cookies[i].split("=")
+ if (cookie[0].indexOf("imname") !== -1)
+ {
+ if (cookie[1] !== 'false' && cookie[1] !== 'undefined' && cookie[1].length)
+ {
+ return cookie[1]
+ }
+ }
+ }
+ }
+ return ""
+ },
+ init: function ()
+ {
+ var name = Main.cookie ()
+ $("#username").val(name)
+ $("#breakbutton").bind("click", Main.go)
+ $(document).bind("keydown", Main.enter)
+ }
+ }
+//$('#theform').each(function(){
+// this.reset();
+// });
+
+
+
+Main.init ()
diff --git a/imgradient_index.html b/imgradient_index.html
new file mode 100644
index 0000000..70a39dc
--- /dev/null
+++ b/imgradient_index.html
@@ -0,0 +1,532 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+ __ __ __ __ __ __ __
+/_/\/\ /_/\/\ /_/\/\ /_/\/\ /_/\/\ /_/\/\ /_/\/\
+\_\ / \_\ / \_\ / \_\ / \_\ / \_\ / \_\ /
+/_/ \ /_/ \ /_/ \ /_/ \ /_/ \ /_/ \ /_/ \
+\_\/\ \ \_\/\ \ \_\/\ \ \_\/\ \ \_\/\ \ \_\/\ \ \_\/\ \
+ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+-->
+
+<html>
+<head>
+<title>GRADIENT PHOTOBLASTER</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta property="og:title" content="GRADIENT PHOTOBLASTER"/>
+<meta property="og:type" content="website"/>
+<meta name=Author content="Pepper .... with help from Jules Welter/LaPlace"/>
+<meta property="og:url" content="http://asdf.us/imgradient/"/>
+<meta property="og:image" content="http://asdf.us/g/experiment/whitegreencrazy.jpg" />
+<meta property="og:site_name" content="asdf.us"/>
+<meta property="og:description" content="choose colors and options, let it flow out of you."/>
+<meta property="fb:admins" content="100001923956223,1709246"/>
+<link href="css/custom-theme/jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />
+<style type="text/css">
+*
+ {
+ padding: 0;
+ margin: 0;
+ font-family: times;
+ }
+body
+ {
+ background-color: #e6e0e0;
+ color: #696969;
+ overflow-x:hidden;
+ }
+h1
+ {
+ font-size: 40px;
+ }
+#brightness-slider,#hue-slider,#saturation-slider,#blur-slider
+ {
+ margin-top:10px;
+ margin-bottom:10px;
+ background-color:#B3B3B3;
+ }
+#stripenumber,#stripeintensity,#percentbeveled
+ {
+ height:10px;
+ width :25px;
+ font-size:9;
+ }
+#controls,#result
+ {
+ padding: 10px;
+ width: 460px;
+ }
+#controls
+ {
+ display:block;
+ font-size: 14px;
+ top: 10px; left: 10px;
+ z-index: 5;
+ background-color: #fff;
+ -moz-box-shadow: 0px 0px 9px 3px mediumseagreen;
+ -webkit-box-shadow: 0px 0px 9px 3px mediumseagreen;
+ box-shadow: 0px 0px 9px 3px mediumseagreen;
+ border: 60px outset #777;
+ }
+#result
+ {
+ background-color: #d6d0d0;
+ top: 10px; left: 580px;
+ z-index: 5;
+ display: none;
+ position: fixed;
+ }
+#instructions
+ {
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ line-height: 18px;
+ z-index: 1;
+ }
+p
+ {
+ background-color: #fff;
+ padding: 10px;
+ font-size: 14px;
+ width: 430px;
+ display: block;
+ }
+#gallery-link
+ {
+ background-color: #fff;
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ padding: 10px;
+ z-index: 10;
+ }
+.error
+ {
+ color: red;
+ font-size: 20px;
+ }
+a
+ {
+ color: #b4d;
+ font-weight: bold;
+ }
+label
+
+ {
+ display: inline-block;
+ width: 300px;
+ padding-right: 10px;
+ text-align: center;
+ }
+input[type=text]
+ {
+ width: 100px;
+ }
+#img-url,#output-url,#img-background
+ {
+ width: 200px;
+ }
+#img-width,#img-height,#img-brightness,#img-saturation,#img-contrast,#img-hue,#img-rotate,#img-blur,#img-tilt
+ {
+ width: 50px;
+ text-align: right;
+ }
+button
+ {
+ padding: 2px 5px;
+ font-size: 16px;
+ width:200px;
+ float:right;
+ font-weight:bold;
+ color:#222222;
+ }
+#result img
+ {
+ max-width: 400px;
+ max-height: 400px;
+ }
+#output-cmd
+ {
+ font-size: 12px;
+ white-space: pre;
+ }
+.shim
+ {
+ height: 10px;
+ clear: both;
+ display: block;
+ }
+#output-url
+ {
+ width: 320px;
+ }
+#likebutton
+ {
+ position: fixed;
+ bottom: 10px;
+ right: 10px;
+ width: 350px;
+ background-color: #fff;
+ padding: 10px;
+ color: #ddd;
+ font-family: times;
+ text-transform: uppercase;
+ }
+.arrow
+ {
+ float:right;
+ white-space:inherit;
+ display:inline;
+ }
+
+</style>
+<body>
+<div id="error"></div>
+<div id="controls">
+<form id="reset" onsubmit="return false">
+<h1>
+ GRADIENT GENERATOR FOR PHOTOBLASTER
+ <img src="http://i.asdf.us/im/4e/angreekcolumnshowcaselg_1315115918_pepper.gif"/>
+ <img src="http://i.asdf.us/im/4e/angreekcolumnshowcaselg_1315115933_pepper.gif"/>
+ <img src="http://i.asdf.us/im/4e/angreekcolumnshowcaselg_1315115941_pepper.gif"/>
+ <img src="http://i.asdf.us/im/4e/angreekcolumnshowcaselg_1315115946_pepper.gif"/>
+ <img src="http://i.asdf.us/im/4e/angreekcolumnshowcaselg_1315115946_pepper.gif"/>
+ <img src="http://i.asdf.us/im/4e/angreekcolumnshowcaselg_1315115946_pepper.gif"/>
+ <img src="http://i.asdf.us/im/4e/angreekcolumnshowcaselg_1315115946_pepper.gif"/>
+ <img src="http://i.asdf.us/im/4e/angreekcolumnshowcaselg_1315115946_pepper.gif"/>
+</h1>
+
+ <span class="shim"></span>
+
+
+ <label><span style="font-weight:bold; color:black;">CHOOSE A GRADIENT TYPE<span class="arrow">&rarr;</span></span></label>
+ <select id="gradient-type">
+ <option selected="selected" value="gradient">STRAIGHT FADE</option>
+ <option value="plasma">PLASMA</option>
+ <!-- <option value="-function sinusoid 4,-90 gradient:">bands</option>-->
+ <option value="canvas">JUST A FLAT COLOR</option>
+ <option value="radial">RADIAL GRADIENT</option>
+ <option value="colorspace">GRAYSCALE PLASMA</option>
+ <option value="plasmawash">PLASMA WASH</option>
+ <option value="gradientwash">GRADIENT WASH</option>
+ <option value="mirrored">MIRRORED PLASMA</option>
+ <option value="noise">RANDOM NOISE</option>
+ </select>
+
+ <span class="shim"></span>
+
+ <label><span style="font-weight:bold">ADJUST THE WIDTH</span><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-width" value="400" /><small>px</small>
+ <br/>
+
+ <span class="shim"></span>
+
+ <label><span style="font-weight:bold">ADJUST THE HEIGHT</span><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-height" value="400" /><small>px</small>
+ <br/>
+
+ <span class="shim"></span>
+
+ <label>CHOOSE SOME COLORS<span class="arrow">&rarr;</span></label>
+ <a href="http://asdf.us/imgrid/colors" target="_blank">list of color names</a>
+
+ <span class="shim"></span>
+
+ <label><span style="font-weight:bold; color:black;">COLOR 1</span><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-color1" value="white" />
+ <br/>
+
+ <span class="shim"></span>
+
+ <label><span style="font-weight:bold; color:black;">COLOR 2</span><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-color2" value="black" />
+ <br/>
+
+ <span class="shim"></span>
+
+ <span style="font-size:11px">
+ <label>BANDS(also called stripes)?<span class="arrow">&rarr;</span></label>
+ <input type="checkbox" id="stripes" value="1" />
+ <small>if yes...</small><label>NUMBER:</label><input value="" type="text" id="stripenumber"></input><small>0-400</small><label>INTENSITY:</label><input value="" type="text" id="stripeintensity"></input><small>0-2000</small>
+ </span>
+
+ <span class="shim"></span>
+ <span class="shim"></span>
+
+ <label>BLURRINESS <small>(0-20)</small><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-blur" value="" />
+ <br/>
+
+ <div class="slider" id="blur-slider"></div>
+
+ <label>HUE <small>(0-200)</small><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-hue" value="" />
+ <br/>
+
+ <div class="slider" id="hue-slider"></div>
+
+ <label>SATURATION <small>(0-200)</small><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-saturation" value="" />
+ <br/>
+
+ <div class="slider" id="saturation-slider"></div>
+
+ <label>BRIGHTNESS <small>(0-200)</small><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-brightness" value="" />
+ <br/>
+
+ <div class="slider" id="brightness-slider"></div>
+
+ <span class="shim"></span>
+
+
+ <label><span style="font-size:11px">CHOOSE A HALFTONE FILTER</span><span class="arrow">&rarr;</span></label>
+ <select id="halftone-type">
+ <option selected="selected" value="">None</option>
+ <option value="checkeredfade">checkered-fade</option>
+ <option value="etchedtransition">etched-transition</option>
+ <option value="bendaydots">benday dots</option>
+ <option value="smallerdots1">smaller dots 1</option>
+ <option value="smallerdots2">smaller dots 2</option>
+ <option value="flatstripes">flat stripes</option>
+ </select>
+
+
+ <span class="shim"></span>
+
+ <label><span style="font-size:11px">ADD A BEVELED BORDER</span><span class="arrow">&rarr;</span></label>
+
+ <select id="bevel-type">
+ <option selected="selected" value="">None</option>
+ <option value="flatout">flat out</option>
+ <option value="flatinner">flat inner</option>
+ <option value="evenlyframed">evely framed</option>
+ <option value="biginner">big inner</option>
+ <option value="bigouter">big outer</option>
+ <option value="dramaticflatout">dramatic flat out</option>
+ <option value="dramaticflatinner">dramatic flat inner</option>
+ </select>
+
+ <span style="font-size:11px"><label>PERCENT BEVELED?</label></span> <input val="" type="text" id="percentbeveled"><small>%</small>
+
+
+ <span class="shim"></span>
+ <span class="shim"></span>
+ <label>FLIP HORIZONTALLY?
+ <span class="arrow">&rarr;</span></label>
+ <input type="checkbox" id="img-flop" value="1" />
+ <br/>
+
+ <label>FLIP VERTICALLY?<span class="arrow">&rarr;</span></label>
+ <input type="checkbox" id="img-flip" value="1" />
+ <br/>
+ <label>TILT<small>(0-360)</small><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-tilt" value="" />&deg;
+ <br/>
+ <span class="shim"></span>
+
+ <label>ROTATE THE CANVAS<small>(0-360)</small><span class="arrow">&rarr;</span></label>
+ <input type="text" id="img-rotate" value="" />&deg;
+ <br/>
+
+ <span class="shim"></span>
+
+ <span class="shim"></span>
+
+ <label>output format:</label>
+ <select id="img-format">
+ <option selected="selected">png</option>
+ <option>jpg</option>
+ <option>gif</option>
+ </select>
+
+ <span class="shim"></span>
+
+ <label>PUT YOUR NAME HERE &gt;&gt;&gt;</label>
+ <input type="text" id="img-name" value="" />
+ <br/>
+
+ <span class="shim"></span>
+
+ <label>&nbsp;</label>
+ <button id="img-generate">GENERATE</button>
+
+ <span class="shim"></span>
+ <br>
+ VIEW AND ARRANGE THE PHOTOBLASTS &rarr; <a href="/im/gallery/" target="_blank">Image Gallery</a><br/>
+ <span class="shim"></span>
+ OPEN THE PHOTOBLASTER EDITOR &rarr; <a href="/im" target="_blank">PHOTOBLASTER</a>
+ <span class="shim"></span>
+ TOP PHOTOBLASTS GO TO THE TUMBLR &rarr; <a href="http://photoblaster.tumblr.com/">Photoblaster Tumblr</a>
+ </p>
+
+</div>
+
+<div id="result">
+ &rarr; <input type="text" id="output-url"/><br/>
+ <span id="output-cmd"></span><br/>
+
+ <img id="output-img" />
+</div>
+</form>
+</div>
+
+<div id="likebutton">
+<div id="fb-root" style="background-color: transparent;"></div><script src="http://connect.facebook.net/en_US/all.js#appId=236917449658413&amp;xfbml=1"></script><fb:like href="http://asdf.us/im/" send="false" width="347" show_faces="true" colorscheme="light" font="" style="background-color: transparent;"></fb:like>
+</div>
+
+<script type="text/javascript" src="/js/jquery.js"></script>
+<script type="text/javascript" src="jquery-ui-1.8.16.custom.min.js"></script>
+<script type="text/javascript">
+
+ $(function() {
+ $( "#blur-slider" ).slider({
+ value:0,
+ min: 0,
+ max: 20,
+ step: 1,
+ slide: function( event, ui ) {
+ $( "#img-blur" ).val(ui.value);
+ }
+ });
+ $( "#img-blur" ).val( $( "#blur-slider" ).slider( "value" ) );
+
+ $( "#brightness-slider" ).slider({
+ value:100,
+ min: 0,
+ max: 200,
+ step: 1,
+ slide: function( event, ui ) {
+ $( "#img-brightness" ).val(ui.value);
+ }
+ });
+ $( "#img-brightness" ).val( $( "#brightness-slider" ).slider( "value" ) );
+
+ $( "#hue-slider" ).slider({
+ value:100,
+ min: 0,
+ max: 200,
+ step: 1,
+ slide: function( event, ui ) {
+ $( "#img-hue" ).val(ui.value);
+ }
+ });
+ $( "#img-hue" ).val($( "#hue-slider" ).slider( "value" ) );
+
+ $( "#saturation-slider" ).slider({
+ value:100,
+ min: 0,
+ max: 200,
+ step: 1,
+ slide: function( event, ui ) {
+ $( "#img-saturation" ).val(ui.value);
+ }
+ });
+ $( "#img-saturation" ).val($( "#saturation-slider" ).slider( "value" ) );
+ });
+
+var Main =
+ {
+ API_HEADER: "#@imgradient",
+ enter: function (e)
+ {
+ if (e.keyCode === 13)
+ Main.go()
+ },
+ go: function ()
+ {
+ $("#output-cmd").html('generating...').show()
+ $("#result").show()
+ var data =
+ {
+ flip: $('#img-flip:checked').val() !== undefined ? "true" : "false",
+ flop: $('#img-flop:checked').val() !== undefined ? "true" : "false",
+ tilt: $('#img-tilt').val(),
+ rotate: $("#img-rotate").val(),
+ subtract: $("#img-subtract").val(),
+ width: $("#img-width").val(),
+ height: $("#img-height").val(),
+ color2: $("#img-color2").val(),
+ color1: $("#img-color1").val(),
+ brightness: $("#img-brightness").val(),
+ saturation: $("#img-saturation").val(),
+ blurriness: $("#img-blur").val(),
+ hue: $("#img-hue").val(),
+ contrast: $("#img-contrast").val(),
+ gradienttype: $('#gradient-type :selected').val(),
+ bevel: $('#bevel-type :selected').val(),
+ percentbeveled: $('#percentbeveled').val(),
+ halftone: $('#halftone-type :selected').val(),
+ stripes: $('#stripes:checked').val() !== undefined ? "true" : "false",
+ stripenumber: $('#stripenumber').val(),
+ stripeintensity: $('#stripeintensity').val(),
+ format: $('#img-format :selected').text(),
+ name: $("#img-name").val(),
+ }
+ if (data.name.length > 0)
+ document.cookie = "imname="+data.name+";path=/;domain=.asdf.us;max-age=1086400"
+ //FIXME
+ $.post("/cgi-bin/im/gradient", data, Main.callback)
+ $("#controls").css('margin',"")
+ },
+ error: function (s)
+ {
+ $("#output-cmd").html("<span class='error'>ERROR: " + s + "</span>").show()
+ $("#output-url").hide()
+ $("#output-img").hide()
+ },
+ callback: function (data)
+ {
+ data = JSON.parse(data)
+ if (data.error){
+ return Main.error(data.error)
+ }
+ $("#output-cmd").html("size: "+Main.filesize(data.size)+"<br/>"+data.height + "x" + data.width)
+ $("#output-url").val(data.url)
+ $("#output-img").hide().attr("src", data.url).fadeIn(700)
+ },
+ filesize: function (size)
+ {
+ if (size < 1024)
+ return size + " bytes"
+ if (size < 1024 * 1024)
+ return Math.floor (size/1024) + " KB"
+ else
+ return Math.floor (size/(1024*1024)) + " MB"
+ },
+ cookie: function ()
+ {
+ if (document.cookie)
+ {
+ var cookies = document.cookie.split(";")
+ for (i in cookies)
+ {
+ var cookie = cookies[i].split("=")
+ if (cookie[0].indexOf("imname") !== -1)
+ {
+ if (cookie[1] !== 'false' && cookie[1] !== 'undefined' && cookie[1].length)
+ {
+ return cookie[1]
+ }
+ }
+ }
+ }
+ return ""
+ },
+ init: function ()
+ {
+ var name = Main.cookie ()
+ $("#img-name").val(name)
+ $("img-generate").click(function(){$('#img-generate').html('whos got a plastic bag')})
+ $("#img-generate").bind("click", Main.go)
+ $(document).bind("keydown", Main.enter)
+ },
+ }
+Main.init ()
+document.getElementById("reset").reset()
+</script>
+<script type="text/javascript" src="http://asdf.us/js/pbembed.js"></script>
+</body>
+</html>
+
diff --git a/imgrid.py b/imgrid.py
new file mode 100755
index 0000000..a751b6b
--- /dev/null
+++ b/imgrid.py
@@ -0,0 +1,356 @@
+#!/usr/bin/python2.7
+
+import sys
+import re
+import os
+import string
+import simplejson as json
+import random
+import time
+import urllib
+import urllib2
+from subprocess import Popen,PIPE,call
+urlencode = urllib.urlencode
+urlopen = urllib2.urlopen
+Request = urllib2.Request
+
+MAX_SIZE = 1024 * 1024 * 1.2 * 1.5
+WORKING_DIR = "/tmp"
+
+BIN_CONVERT = "/usr/bin/convert"
+BIN_COMPOSITE = "/usr/bin/composite"
+BIN_IDENTIFY = "/usr/bin/identify"
+THREEDROTATE = "./3Drotate"
+GRID = "./grid"
+DB_TAG = "grid";
+
+DEFAULT_HEIGHT = 400
+DEFAULT_WIDTH = 600
+DEFAULT_LINE_COLOR = "silver"
+DEFAULT_FINALFORMAT = "png"
+
+#{{{Utility functions
+def bool_correct(s):
+ if re.match(r'^false$', s, re.IGNORECASE):
+ return False
+ elif re.match(r'^true$', s, re.IGNORECASE):
+ return True
+ else:
+ return s
+
+class dotdict(dict):
+ """dot.notation access to dictionary attributes"""
+ def __getattr__(self, attr):
+ return self.get(attr)
+ __setattr__= dict.__setitem__
+ __delattr__= dict.__delitem__
+
+def get_mimetype(f):
+ try:
+ mimetype = Popen(
+ [BIN_IDENTIFY, f], stdout=PIPE
+ ).communicate()[0].split(" ")[1].lower()
+ return mimetype
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+def sanitize (str):
+ return re.sub(r'\W+', '', str)
+
+def now():
+ return int(time.time())
+
+def browser_request (url, data=None):
+ headers = {
+ 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
+ 'Accept': '*/*',
+ }
+ try:
+ req = Request(url, data, headers)
+ response = urlopen(req)
+ except IOError, e:
+ if hasattr(e, 'code'):
+ sys.stderr.write( '%s - ERROR %s' % (url, e.code) )
+ raise;
+ return None
+ else:
+ return response
+
+def download(url, destination, max_size=MAX_SIZE):
+ response = browser_request(url, None)
+ rawimg = response.read()
+ if len(rawimg) == 0:
+ sys.stderr.write("got zero-length file")
+ raise;
+ if len(rawimg) > max_size:
+ sys.stderr.write("file too big: max size {} KB / {} is {} KB".format(
+ str(MAX_SIZE/1024),
+ destination,
+ str(len(rawimg)/1024)
+ ))
+ raise;
+ f = open(destination, "w")
+ f.write(rawimg)
+ f.close()
+
+def file_size (filepath):
+ try:
+ return os.stat(file)[6]
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+def gif_frames(filepath):
+ try:
+ info = Popen([BIN_IDENTIFY,filepath], stdout=PIPE).communicate()[0]
+ frames = filter((lambda x: x), map(
+ (lambda x: x.split(" ")[0]),
+ (info).split('\n')
+ ))
+ return frames
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+#}}}
+
+class Imgrid():
+ def __init__(self, params):
+ self.params = {}
+ self.tag = "imGrid"
+ self.now = now()
+ self.files_created = []
+ self.commands = [];
+ self.required_keys = [
+#{{{ required_keys
+ "width",
+ "height",
+ "linethickness",
+ "opacity",
+ "linecolor",
+ "spacing",
+ "vlines",
+ "hlines",
+ "shadow",
+ "bgimage",
+ "bgcolor",
+ "imageinstead",
+ "planebgcolor",
+ "planebgimage",
+ "swing",
+ "tilt",
+ "roll",
+ "zoom",
+ "skycolor",
+ "transition",
+ "trim",
+ "format",
+ "username"
+#}}}
+ ]
+ for k in self.required_keys:
+ if k in params:
+ if k in [ 'bgimage', 'planebgimage', 'imageinstead' ] and bool_correct(params[k]):
+ self.params[k] = {}
+ self.params[k]['url'] = params[k]
+ self.params[k]['filename'] = "IMGRIDTMP{}_{}".format(now(), k)
+
+ self.params[k]['path'] = os.path.join(WORKING_DIR, self.params[k]['filename'])
+ try:
+ download(self.params[k]['url'], self.params[k]['path'])
+ self.files_created.append(self.params[k]['path'])
+ self.params[k]['mimetype'] = get_mimetype(self.params[k]['path'])
+ frames = gif_frames(self.params[k]['path'])
+ if len(frames) > 1:
+ self.params[k]['path'] = random.choice(frames)
+
+
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+ else:
+ self.params[k] = bool_correct(sanitize(params[k]))
+ else:
+ self.params[k] = False;
+
+ self.params = dotdict(self.params)
+
+ self.basename = self._get_filename();
+
+ if not self.params.finalformat:
+ self.params.finalformat = DEFAULT_FINALFORMAT
+ self.filename = "{}.{}".format(self.basename, self.params.finalformat)
+ #final filepath is stored in self.filepath
+ self.filepath = os.path.join(WORKING_DIR, self.filename)
+
+ def _get_filename(self):
+ return "{}_{}_{}".format(
+ self.tag,
+ now(),
+ self.params.username or ""
+ );
+
+ #makes a canvas file...step 1 (if not bgimage)
+ def _make_canvas(self):
+ dimensions = "{}x{}".format(
+ self.params.width or DEFAULT_WIDTH,
+ self.params.height or DEFAULT_HEIGHT
+ )
+ if self.params.bgimage:
+ return
+ bgcolor = "xc:{}".format(self.params.bgcolor or 'transparent')
+ cmd = [ BIN_CONVERT, "-size", dimensions, bgcolor, self.filepath ]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+ #2nd step-- run grid
+ def _grid_command(self):
+ cmd = [GRID]
+ if self.params.spacing:
+ if self.params.vlines:
+ width = 2 * int(self.params.width or DEFAULT_WIDTH)
+ cmd += ["-s","{},{}".format(self.params.spacing,width)]
+ elif self.params.hlines:
+ height = 2 * int(self.params.height or DEFAULT_HEIGHT)
+ cmd += ["-s", "{},{}".format(height,self.params.spacing)]
+ else:
+ cmd += ["-s",self.params.spacing]
+ cmd += [ "-c", self.params.linecolor or DEFAULT_LINE_COLOR]
+ if self.params.linethickness: cmd += ['-t',self.params.linethickness]
+ if self.params.opacity: cmd += ['-o',self.params.opacity]
+ cmd += [ self.filepath, self.filepath ]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+ def _shadow_cmd(self):
+ #convert 1.png \( +clone -background black -shadow 110x1+9+9 \) +swap -background none -layers merge +repage 2.png
+ cmd = [
+ BIN_CONVERT,
+ self.filepath,
+ "(","+clone","-background","black","-shadow","100x2+20+10",")",
+ "+swap","-background","none","-layers","merge","+repage" ,
+ self.filepath
+ ]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+
+ def _threed_rotate_cmd (self):
+ #3rd step--run 3Drotate
+ cmd = [THREEDROTATE]
+ if self.params.swing: cmd += ["pan={}".format(self.params.swing)]
+ if self.params.tilt: cmd += ["tilt={}".format(self.params.tilt)]
+ if self.params.roll: cmd += ["roll={}".format(self.params.roll)]
+ if self.params.zoom: cmd += ["zoom={}".format(self.params.zoom)]
+ if cmd == [THREEDROTATE]: #if nothing has been added
+ return
+ if self.params.planebgcolor and not self.params.planebgimage:
+ cmd += ["bgcolor={}".format(self.params.planebgcolor)]
+ else:
+ cmd += ["bgcolor=none"]
+ cmd += ["skycolor={}".format(self.params.skycolor or 'none')]
+ if self.params.transition: cmd += ["vp={}",self.params.transition]
+ cmd += [ self.filepath, self.filepath ]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+
+ def _trim_cmd (self):
+ if self.params.trim:
+ cmd = [BIN_CONVERT, self.filepath, "-trim", "+repage", self.filepath]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+
+ def _prepare_gridimage(self, image):
+ if image['mimetype'] != 'png':
+ cmd = [BIN_CONVERT, image['path'], self.filepath]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+ else:
+ cmd = ['cp', image['path'], self.filepath]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+
+ def _overlay_planebgimage(self):
+ cmd = [
+ BIN_COMPOSITE,
+ "-compose", "Dst_Over", "-gravity", "center",
+ self.params.planebgimage["path"],
+ self.filepath,
+ self.filepath
+ ]
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+
+ def _cleanup(self):
+ if not len(self.files_created):
+ pass
+ cmd = ["rm", "-f"] + self.files_created
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd))
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+
+ def create(self):
+ if self.params.bgimage:
+ self._prepare_gridimage(self.params.bgimage)
+ self._grid_command()
+ elif self.params.imageinstead:
+ self._prepare_gridimage(self.params.imageinstead)
+ else:
+ self._make_canvas()
+ self._grid_command()
+ if self.params.shadow: self._shadow_cmd()
+ self._threed_rotate_cmd()
+ if self.params.planebgimage: self._overlay_planebgimage()
+ if self.params.trim: self._trim_cmd()
+ self._cleanup()
+
+if __name__ == "__main__":
+ g = Imgrid({
+ 'bgimage' : 'http://i.asdf.us/im/1a/imBreak_1424909483_xx_abridged___.gif',
+ 'planebgimage' : 'http://i.imgur.com/FICZtph.png',
+ 'tilt' : '30',
+ 'spacing' : '30',
+ 'hlines' : 'true',
+ 'roll' : '30',
+ 'shadow' : 'true',
+ 'trim' : 'true'
+ })
+ g.create()
+ print g.now
+ print g.filepath
+ print g.commands
diff --git a/imgrid_main.js b/imgrid_main.js
new file mode 100644
index 0000000..29114f2
--- /dev/null
+++ b/imgrid_main.js
@@ -0,0 +1,121 @@
+var Main =
+ {
+ API_HEADER: "#@imgrid",
+ generating: false,
+ enter: function (e)
+ {
+ if (Main.generating)
+ return
+ if (e.keyCode === 13)
+ Main.go()
+ },
+ go: function ()
+ {
+ if (Main.generating)
+ return
+ Main.generating = true
+ var theloader = '<span style="width:100%;margin-right:40%"><img style="width:140px;height:120px;display:inline;" src="generating.gif"></img></span>'
+ $("#output-cmd").html(theloader).show()
+ if($('#transition :selected').val() === 'tile'||$('#transition :selected').val()=== 'random')
+ {
+ $('#output-cmd').append("<br><span style='color:red'>WARNING: THIS REQUEST MIGHT TAKE A WHILE</span>")
+ }
+ $('.results').show()
+ $("#output-img").show()
+ $("#output-url").show()
+ $("#result").show()
+ var data =
+ {
+ width: $("#img-width").val(),
+ height: $("#img-height").val(),
+ linethickness: $("#line-thickness").val(),
+ opacity: $("#line-opacity").val(),
+ linecolor: $("#line-color").val(),
+ spacing: $("#line-spacing").val(),
+ vlines: $('#v-lines:checked').val() !== undefined ? "true" : "false",
+ hlines: $('#h-lines:checked').val() !== undefined ? "true" : "false",
+ shadow: $('#shadow:checked').val() !== undefined ? "true" : "false",
+ bgimage: $("#bg-image").val(),
+ bgcolor: $("#bg-color").val(),
+ imageinstead: $("#imageinstead").val(),
+ planebgcolor: $("#planebgcolor").val(),
+ skycolor: $("#skycolor").val(),
+ planebgimage: $("#planebgimage").val(),
+ transition: $('#transition :selected').val(),
+ swing: $("#swing").val(),
+ tilt: $("#tilt").val(),
+ roll: $("#roll").val(),
+ zoom: $("#zoom").val(),
+ trim: $("#trim:checked").val() !== undefined ? "true" : "false",
+ format: $('#format :selected').val(),
+ username: $('#username').val()
+ }
+ if (data.transition == 'infinite'){
+ $('#genbutton').append("<span style='color:red'>WARNING:This might take a while</span>")}
+ if (data.username.length > 0)
+ document.cookie = "imname="+data.username+";path=/;domain=.asdf.us;max-age=1086400"
+ $.post("localhost:8999/imgrid", data, Main.callback)
+ },
+ error: function (s)
+ {
+ $("#output-cmd").html("<span class='error'>ERROR: " + s + "</span>").show()
+ $("#output-url").hide()
+ $("#output-img").hide()
+ },
+ filesize: function (size)
+ {
+ if (size < 1024)
+ return size.toString() + " bytes"
+ if (size < 1024 * 1024)
+ return Math.floor (size/1024).toString() + " KB"
+ else
+ return Math.floor (size/(1024*1024)).toString() + " MB"
+ },
+ callback: function (data)
+ {
+ data = JSON.parse(data)
+ $("#output-cmd").html('')
+ $("#output-img").html("<a target=_blank href='"+data.url+"'>"+"<img src='"+data.url+"'></img><br>"+"</a>");
+ $("#output-url").val(data.url)
+ $("#output-info").html('-ACTUAL SIZE-<br>'+Main.filesize(data.size)+'<br>'+data.width+'<br>'+data.height+'<br><br>'+'<span style="float:right">see more at &rarr;<a href="http://asdf.us/im/gallery">photoblaster gallery</a></span>'+'<br>')
+ Main.generating = false
+ },
+ cookie: function ()
+ {
+ if (document.cookie)
+ {
+ var cookies = document.cookie.split(";")
+ for (i in cookies)
+ {
+ var cookie = cookies[i].split("=")
+ if (cookie[0].indexOf("imname") !== -1)
+ {
+ if (cookie[1] !== 'false' && cookie[1] !== 'undefined' && cookie[1].length)
+ {
+ return cookie[1]
+ }
+ }
+ }
+ }
+ return ""
+ },
+ init: function ()
+ {
+ var name = Main.cookie ()
+ $("#username").val(name)
+ if (name)
+ {
+// $("#userlink").show()
+ // $("#userlink a").attr("href", "/im/gallery/?name="+name).html(name+"'s photoblasts")
+ }
+ $("#generate").bind("click", Main.go)
+ $(document).bind("keydown", Main.enter)
+ }
+ }
+$('#theform').each(function(){
+ this.reset();
+ });
+
+
+
+Main.init ()
diff --git a/pbserver.py b/pbserver.py
new file mode 100755
index 0000000..82198ab
--- /dev/null
+++ b/pbserver.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python2.7
+from bottle import route, run, post, request
+
+from gradient import Gradient
+from imgrid import Imgrid
+from breaker import Breaker
+from s3config import AWS_SECRET_ACCESS_KEY, AWS_ACCESS_KEY_ID, BUCKET_NAME
+
+
+import os
+import sys
+import db
+import s3
+import mimetypes
+import sha
+from subprocess import call, Popen, PIPE
+import simplejson as json
+BIN_IDENTIFY = "/usr/bin/identify"
+
+#try:
+# DB = db.db ()
+#except Exception as e:
+# sys.stderr.write("Could not connect to db:\n{}".format(e))
+# sys.exit(1);
+BASE_URL = "http://i.asdf.us"
+
+def hashdir(filename):
+ return sha.new(filename).hexdigest()[:2]
+
+def file_size (filepath):
+ try:
+ return os.stat(file)[6]
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+def bin_identify (filepath):
+ ident = Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0]
+ partz = ident.split(" ")
+ width,height = partz[2].split("x")
+ return width, height
+
+
+def cleanup(filepath):
+ try:
+ call(['rm', filepath])
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+
+def moveToS3(filename,objectname):
+ conn = s3.AWSAuthConnection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
+ filedata = open(filename, 'rb').read()
+ content_type = mimetypes.guess_type(filename)[0]
+ try:
+ conn.put(BUCKET_NAME, objectname, s3.S3Object(filedata),
+ {
+ 'x-amz-acl': 'public-read',
+ 'Content-Type': content_type or 'text/plain',
+ 'x-amz-storage-class': 'REDUCED_REDUNDANCY'
+ }
+ );
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise
+
+def insert_cmd (date, remote_addr, username, url, directory, oldfile, newfile, cmd, dataobj, tag):
+ try:
+ sql = "INSERT INTO im_cmd "
+ sql += "(date, remote_addr, name, url, dir, oldfile, newfile, cmd, dataobj, tag) "
+ sql += "VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
+ #or "NULL"
+ args = (now(), remote_addr, username, url, directory, oldfile, newfile, cmd, dataobj, tag)
+ #args = (now(), os.environ['REMOTE_ADDR'], name, url, dir, oldfile, newfile, " ".join(cmd),dataobj)
+ DB.execute(sql, args)
+ except Exception as e:
+ sys.stderr.write(str(e))
+ return
+
+@post('/gradient')
+def gradient():
+ try:
+ im = Gradient(request.forms)
+ im.create();
+ directory = hashdir(im.filename)
+ dimensions = bin_identify(im.filepath)
+ objectname = "{}/{}".format(directory, im.filename)
+# s3move(im.filepath, objectname)
+ return(im.filepath, objectname)
+# return "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}".format(
+# insert_cmd(
+# im.now,
+# request.environ.get('REMOTE_ADDR'),
+# im.params['username'] or "NULL",
+# "NULL",
+# directory,
+# "NULL",
+# im.filename,
+# ";".join(im.commands),
+# json.dumps(im.params),
+# im.tag,
+# )
+ return json.loads({
+ 'url' : "{}/{}".format(BASE_URL, objectname)
+ 'size' : file_size(im.filepath),
+ 'width' : "{}px".format(dimensions[0]),
+ 'height' : "{}px".format(dimensions[1]),
+ })
+ #URL, FILESIZE, WIDTH, HEIGHT to replace index.html
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+@post('/imgrid')
+def imgrid():
+ try:
+ im = Imgrid(request.forms)
+ im.create();
+ directory = hashdir(im.filename)
+ dimensions = bin_identify(im.filepath)
+ objectname = "{}/{}".format(directory, im.filename)
+# s3move(im.filepath, objectname)
+ return(im.filepath, objectname)
+# return "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}".format(
+# insert_cmd(
+# im.now,
+# request.environ.get('REMOTE_ADDR'),
+# im.params['username'] or "NULL",
+# im.params.imageinstead or im.params.bgimage or im.params.planebgimage or "NULL",
+# directory,
+# "NULL",
+# im.filename,
+# ";".join(im.commands),
+# json.dumps(im.params),
+# im.tag,
+# )
+ return json.loads({
+ 'url' : "{}/{}".format(BASE_URL, objectname)
+ 'size' : file_size(im.filepath),
+ 'width' : "{}px".format(dimensions[0]),
+ 'height' : "{}px".format(dimensions[1]),
+ })
+ #URL, FILESIZE, WIDTH, HEIGHT to replace main.js
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+@post('/breaker')
+def breaker():
+ try:
+ im = Breaker(request.forms)
+ im.create();
+ directory = hashdir(im.filename)
+ dimensions = bin_identify(im.filepath)
+ objectname = "{}/{}".format(directory, im.filename)
+# s3move(im.filepath, objectname)
+ return(im.filepath, objectname)
+# return "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}".format(
+# insert_cmd(
+# im.now,
+# request.environ.get('REMOTE_ADDR'),
+# im.params['username'] or "NULL",
+# im.params['url'],
+# directory,
+# "NULL",
+# im.filename,
+# ";".join(im.commands),
+# json.dumps(im.params),
+# im.tag,
+# )
+ return json.loads({
+ 'url' : "{}/{}".format(BASE_URL, objectname)
+ 'size' : file_size(im.filepath),
+ 'width' : "{}px".format(dimensions[0]),
+ 'height' : "{}px".format(dimensions[1]),
+ })
+ #URL, FILESIZE, WIDTH, HEIGHT to replace main.js make sure you use px
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+run(host='0.0.0.0', port=8999, debug=True)
diff --git a/s3.py b/s3.py
new file mode 100644
index 0000000..1c2b09e
--- /dev/null
+++ b/s3.py
@@ -0,0 +1,618 @@
+#!/usr/bin/env python
+
+# This software code is made available "AS IS" without warranties of any
+# kind. You may copy, display, modify and redistribute the software
+# code either by itself or as incorporated into your code; provided that
+# you do not remove any proprietary notices. Your use of this software
+# code is at your own risk and you waive any claim against Amazon
+# Digital Services, Inc. or its affiliates with respect to your use of
+# this software code. (c) 2006-2007 Amazon Digital Services, Inc. or its
+# affiliates.
+
+import base64
+import hmac
+import httplib
+import re
+import sha
+import sys
+import time
+import urllib
+import urlparse
+import xml.sax
+
+DEFAULT_HOST = 's3.amazonaws.com'
+PORTS_BY_SECURITY = { True: 443, False: 80 }
+METADATA_PREFIX = 'x-amz-meta-'
+AMAZON_HEADER_PREFIX = 'x-amz-'
+
+# generates the aws canonical string for the given parameters
+def canonical_string(method, bucket="", key="", query_args={}, headers={}, expires=None):
+ interesting_headers = {}
+ for header_key in headers:
+ lk = header_key.lower()
+ if lk in ['content-md5', 'content-type', 'date'] or lk.startswith(AMAZON_HEADER_PREFIX):
+ interesting_headers[lk] = headers[header_key].strip()
+
+ # these keys get empty strings if they don't exist
+ if not interesting_headers.has_key('content-type'):
+ interesting_headers['content-type'] = ''
+ if not interesting_headers.has_key('content-md5'):
+ interesting_headers['content-md5'] = ''
+
+ # just in case someone used this. it's not necessary in this lib.
+ if interesting_headers.has_key('x-amz-date'):
+ interesting_headers['date'] = ''
+
+ # if you're using expires for query string auth, then it trumps date
+ # (and x-amz-date)
+ if expires:
+ interesting_headers['date'] = str(expires)
+
+ sorted_header_keys = interesting_headers.keys()
+ sorted_header_keys.sort()
+
+ buf = "%s\n" % method
+ for header_key in sorted_header_keys:
+ if header_key.startswith(AMAZON_HEADER_PREFIX):
+ buf += "%s:%s\n" % (header_key, interesting_headers[header_key])
+ else:
+ buf += "%s\n" % interesting_headers[header_key]
+
+ # append the bucket if it exists
+ if bucket != "":
+ buf += "/%s" % bucket
+
+ # add the key. even if it doesn't exist, add the slash
+ buf += "/%s" % urllib.quote_plus(key)
+
+ # handle special query string arguments
+
+ if query_args.has_key("acl"):
+ buf += "?acl"
+ elif query_args.has_key("torrent"):
+ buf += "?torrent"
+ elif query_args.has_key("logging"):
+ buf += "?logging"
+ elif query_args.has_key("location"):
+ buf += "?location"
+
+ return buf
+
+# computes the base64'ed hmac-sha hash of the canonical string and the secret
+# access key, optionally urlencoding the result
+def encode(aws_secret_access_key, str, urlencode=False):
+ b64_hmac = base64.encodestring(hmac.new(aws_secret_access_key, str, sha).digest()).strip()
+ if urlencode:
+ return urllib.quote_plus(b64_hmac)
+ else:
+ return b64_hmac
+
+def merge_meta(headers, metadata):
+ final_headers = headers.copy()
+ for k in metadata.keys():
+ final_headers[METADATA_PREFIX + k] = metadata[k]
+
+ return final_headers
+
+# builds the query arg string
+def query_args_hash_to_string(query_args):
+ query_string = ""
+ pairs = []
+ for k, v in query_args.items():
+ piece = k
+ if v != None:
+ piece += "=%s" % urllib.quote_plus(str(v))
+ pairs.append(piece)
+
+ return '&'.join(pairs)
+
+
+class CallingFormat:
+ PATH = 1
+ SUBDOMAIN = 2
+ VANITY = 3
+
+ def build_url_base(protocol, server, port, bucket, calling_format):
+ url_base = '%s://' % protocol
+
+ if bucket == '':
+ url_base += server
+ elif calling_format == CallingFormat.SUBDOMAIN:
+ url_base += "%s.%s" % (bucket, server)
+ elif calling_format == CallingFormat.VANITY:
+ url_base += bucket
+ else:
+ url_base += server
+
+ url_base += ":%s" % port
+
+ if (bucket != '') and (calling_format == CallingFormat.PATH):
+ url_base += "/%s" % bucket
+
+ return url_base
+
+ build_url_base = staticmethod(build_url_base)
+
+
+
+class Location:
+ DEFAULT = None
+ EU = 'EU'
+
+
+
+class AWSAuthConnection:
+ def __init__(self, aws_access_key_id, aws_secret_access_key, is_secure=True,
+ server=DEFAULT_HOST, port=None, calling_format=CallingFormat.SUBDOMAIN):
+
+ if not port:
+ port = PORTS_BY_SECURITY[is_secure]
+
+ self.aws_access_key_id = aws_access_key_id
+ self.aws_secret_access_key = aws_secret_access_key
+ self.is_secure = is_secure
+ self.server = server
+ self.port = port
+ self.calling_format = calling_format
+
+ def create_bucket(self, bucket, headers={}):
+ return Response(self._make_request('PUT', bucket, '', {}, headers))
+
+ def create_located_bucket(self, bucket, location=Location.DEFAULT, headers={}):
+ if location == Location.DEFAULT:
+ body = ""
+ else:
+ body = "<CreateBucketConstraint><LocationConstraint>" + \
+ location + \
+ "</LocationConstraint></CreateBucketConstraint>"
+ return Response(self._make_request('PUT', bucket, '', {}, headers, body))
+
+ def check_bucket_exists(self, bucket):
+ return self._make_request('HEAD', bucket, '', {}, {})
+
+ def list_bucket(self, bucket, options={}, headers={}):
+ return ListBucketResponse(self._make_request('GET', bucket, '', options, headers))
+
+ def delete_bucket(self, bucket, headers={}):
+ return Response(self._make_request('DELETE', bucket, '', {}, headers))
+
+ def put(self, bucket, key, object, headers={}):
+ if not isinstance(object, S3Object):
+ object = S3Object(object)
+
+ return Response(
+ self._make_request(
+ 'PUT',
+ bucket,
+ key,
+ {},
+ headers,
+ object.data,
+ object.metadata))
+
+ def get(self, bucket, key, headers={}):
+ return GetResponse(
+ self._make_request('GET', bucket, key, {}, headers))
+
+ def delete(self, bucket, key, headers={}):
+ return Response(
+ self._make_request('DELETE', bucket, key, {}, headers))
+
+ def get_bucket_logging(self, bucket, headers={}):
+ return GetResponse(self._make_request('GET', bucket, '', { 'logging': None }, headers))
+
+ def put_bucket_logging(self, bucket, logging_xml_doc, headers={}):
+ return Response(self._make_request('PUT', bucket, '', { 'logging': None }, headers, logging_xml_doc))
+
+ def get_bucket_acl(self, bucket, headers={}):
+ return self.get_acl(bucket, '', headers)
+
+ def get_acl(self, bucket, key, headers={}):
+ return GetResponse(
+ self._make_request('GET', bucket, key, { 'acl': None }, headers))
+
+ def put_bucket_acl(self, bucket, acl_xml_document, headers={}):
+ return self.put_acl(bucket, '', acl_xml_document, headers)
+
+ def put_acl(self, bucket, key, acl_xml_document, headers={}):
+ return Response(
+ self._make_request(
+ 'PUT',
+ bucket,
+ key,
+ { 'acl': None },
+ headers,
+ acl_xml_document))
+
+ def list_all_my_buckets(self, headers={}):
+ return ListAllMyBucketsResponse(self._make_request('GET', '', '', {}, headers))
+
+ def get_bucket_location(self, bucket):
+ return LocationResponse(self._make_request('GET', bucket, '', {'location' : None}))
+
+ # end public methods
+
+ def _make_request(self, method, bucket='', key='', query_args={}, headers={}, data='', metadata={}):
+
+ server = ''
+ if bucket == '':
+ server = self.server
+ elif self.calling_format == CallingFormat.SUBDOMAIN:
+ server = "%s.%s" % (bucket, self.server)
+ elif self.calling_format == CallingFormat.VANITY:
+ server = bucket
+ else:
+ server = self.server
+
+ path = ''
+
+ if (bucket != '') and (self.calling_format == CallingFormat.PATH):
+ path += "/%s" % bucket
+
+ # add the slash after the bucket regardless
+ # the key will be appended if it is non-empty
+ path += "/%s" % urllib.quote_plus(key)
+
+
+ # build the path_argument string
+ # add the ? in all cases since
+ # signature and credentials follow path args
+ if len(query_args):
+ path += "?" + query_args_hash_to_string(query_args)
+
+ is_secure = self.is_secure
+ host = "%s:%d" % (server, self.port)
+ while True:
+ if (is_secure):
+ connection = httplib.HTTPSConnection(host)
+ else:
+ connection = httplib.HTTPConnection(host)
+
+ final_headers = merge_meta(headers, metadata);
+ # add auth header
+ self._add_aws_auth_header(final_headers, method, bucket, key, query_args)
+
+ connection.request(method, path, data, final_headers)
+ resp = connection.getresponse()
+ if resp.status < 300 or resp.status >= 400:
+ return resp
+ # handle redirect
+ location = resp.getheader('location')
+ if not location:
+ return resp
+ # (close connection)
+ resp.read()
+ scheme, host, path, params, query, fragment \
+ = urlparse.urlparse(location)
+ if scheme == "http": is_secure = True
+ elif scheme == "https": is_secure = False
+ else: raise invalidURL("Not http/https: " + location)
+ if query: path += "?" + query
+ # retry with redirect
+
+ def _add_aws_auth_header(self, headers, method, bucket, key, query_args):
+ if not headers.has_key('Date'):
+ headers['Date'] = time.strftime("%a, %d %b %Y %X GMT", time.gmtime())
+
+ c_string = canonical_string(method, bucket, key, query_args, headers)
+ headers['Authorization'] = \
+ "AWS %s:%s" % (self.aws_access_key_id, encode(self.aws_secret_access_key, c_string))
+
+
+class QueryStringAuthGenerator:
+ # by default, expire in 1 minute
+ DEFAULT_EXPIRES_IN = 60
+
+ def __init__(self, aws_access_key_id, aws_secret_access_key, is_secure=True,
+ server=DEFAULT_HOST, port=None, calling_format=CallingFormat.SUBDOMAIN):
+
+ if not port:
+ port = PORTS_BY_SECURITY[is_secure]
+
+ self.aws_access_key_id = aws_access_key_id
+ self.aws_secret_access_key = aws_secret_access_key
+ if (is_secure):
+ self.protocol = 'https'
+ else:
+ self.protocol = 'http'
+
+ self.is_secure = is_secure
+ self.server = server
+ self.port = port
+ self.calling_format = calling_format
+ self.__expires_in = QueryStringAuthGenerator.DEFAULT_EXPIRES_IN
+ self.__expires = None
+
+ # for backwards compatibility with older versions
+ self.server_name = "%s:%s" % (self.server, self.port)
+
+ def set_expires_in(self, expires_in):
+ self.__expires_in = expires_in
+ self.__expires = None
+
+ def set_expires(self, expires):
+ self.__expires = expires
+ self.__expires_in = None
+
+ def create_bucket(self, bucket, headers={}):
+ return self.generate_url('PUT', bucket, '', {}, headers)
+
+ def list_bucket(self, bucket, options={}, headers={}):
+ return self.generate_url('GET', bucket, '', options, headers)
+
+ def delete_bucket(self, bucket, headers={}):
+ return self.generate_url('DELETE', bucket, '', {}, headers)
+
+ def put(self, bucket, key, object, headers={}):
+ if not isinstance(object, S3Object):
+ object = S3Object(object)
+
+ return self.generate_url(
+ 'PUT',
+ bucket,
+ key,
+ {},
+ merge_meta(headers, object.metadata))
+
+ def get(self, bucket, key, headers={}):
+ return self.generate_url('GET', bucket, key, {}, headers)
+
+ def delete(self, bucket, key, headers={}):
+ return self.generate_url('DELETE', bucket, key, {}, headers)
+
+ def get_bucket_logging(self, bucket, headers={}):
+ return self.generate_url('GET', bucket, '', { 'logging': None }, headers)
+
+ def put_bucket_logging(self, bucket, logging_xml_doc, headers={}):
+ return self.generate_url('PUT', bucket, '', { 'logging': None }, headers)
+
+ def get_bucket_acl(self, bucket, headers={}):
+ return self.get_acl(bucket, '', headers)
+
+ def get_acl(self, bucket, key='', headers={}):
+ return self.generate_url('GET', bucket, key, { 'acl': None }, headers)
+
+ def put_bucket_acl(self, bucket, acl_xml_document, headers={}):
+ return self.put_acl(bucket, '', acl_xml_document, headers)
+
+ # don't really care what the doc is here.
+ def put_acl(self, bucket, key, acl_xml_document, headers={}):
+ return self.generate_url('PUT', bucket, key, { 'acl': None }, headers)
+
+ def list_all_my_buckets(self, headers={}):
+ return self.generate_url('GET', '', '', {}, headers)
+
+ def make_bare_url(self, bucket, key=''):
+ full_url = self.generate_url(self, bucket, key)
+ return full_url[:full_url.index('?')]
+
+ def generate_url(self, method, bucket='', key='', query_args={}, headers={}):
+ expires = 0
+ if self.__expires_in != None:
+ expires = int(time.time() + self.__expires_in)
+ elif self.__expires != None:
+ expires = int(self.__expires)
+ else:
+ raise "Invalid expires state"
+
+ canonical_str = canonical_string(method, bucket, key, query_args, headers, expires)
+ encoded_canonical = encode(self.aws_secret_access_key, canonical_str)
+
+ url = CallingFormat.build_url_base(self.protocol, self.server, self.port, bucket, self.calling_format)
+
+ url += "/%s" % urllib.quote_plus(key)
+
+ query_args['Signature'] = encoded_canonical
+ query_args['Expires'] = expires
+ query_args['AWSAccessKeyId'] = self.aws_access_key_id
+
+ url += "?%s" % query_args_hash_to_string(query_args)
+
+ return url
+
+
+class S3Object:
+ def __init__(self, data, metadata={}):
+ self.data = data
+ self.metadata = metadata
+
+class Owner:
+ def __init__(self, id='', display_name=''):
+ self.id = id
+ self.display_name = display_name
+
+class ListEntry:
+ def __init__(self, key='', last_modified=None, etag='', size=0, storage_class='', owner=None):
+ self.key = key
+ self.last_modified = last_modified
+ self.etag = etag
+ self.size = size
+ self.storage_class = storage_class
+ self.owner = owner
+
+class CommonPrefixEntry:
+ def __init(self, prefix=''):
+ self.prefix = prefix
+
+class Bucket:
+ def __init__(self, name='', creation_date=''):
+ self.name = name
+ self.creation_date = creation_date
+
+class Response:
+ def __init__(self, http_response):
+ self.http_response = http_response
+ # you have to do this read, even if you don't expect a body.
+ # otherwise, the next request fails.
+ self.body = http_response.read()
+ if http_response.status >= 300 and self.body:
+ self.message = self.body
+ else:
+ self.message = "%03d %s" % (http_response.status, http_response.reason)
+
+
+
+class ListBucketResponse(Response):
+ def __init__(self, http_response):
+ Response.__init__(self, http_response)
+ if http_response.status < 300:
+ handler = ListBucketHandler()
+ xml.sax.parseString(self.body, handler)
+ self.entries = handler.entries
+ self.common_prefixes = handler.common_prefixes
+ self.name = handler.name
+ self.marker = handler.marker
+ self.prefix = handler.prefix
+ self.is_truncated = handler.is_truncated
+ self.delimiter = handler.delimiter
+ self.max_keys = handler.max_keys
+ self.next_marker = handler.next_marker
+ else:
+ self.entries = []
+
+class ListAllMyBucketsResponse(Response):
+ def __init__(self, http_response):
+ Response.__init__(self, http_response)
+ if http_response.status < 300:
+ handler = ListAllMyBucketsHandler()
+ xml.sax.parseString(self.body, handler)
+ self.entries = handler.entries
+ else:
+ self.entries = []
+
+class GetResponse(Response):
+ def __init__(self, http_response):
+ Response.__init__(self, http_response)
+ response_headers = http_response.msg # older pythons don't have getheaders
+ metadata = self.get_aws_metadata(response_headers)
+ self.object = S3Object(self.body, metadata)
+
+ def get_aws_metadata(self, headers):
+ metadata = {}
+ for hkey in headers.keys():
+ if hkey.lower().startswith(METADATA_PREFIX):
+ metadata[hkey[len(METADATA_PREFIX):]] = headers[hkey]
+ del headers[hkey]
+
+ return metadata
+
+class LocationResponse(Response):
+ def __init__(self, http_response):
+ Response.__init__(self, http_response)
+ if http_response.status < 300:
+ handler = LocationHandler()
+ xml.sax.parseString(self.body, handler)
+ self.location = handler.location
+
+class ListBucketHandler(xml.sax.ContentHandler):
+ def __init__(self):
+ self.entries = []
+ self.curr_entry = None
+ self.curr_text = ''
+ self.common_prefixes = []
+ self.curr_common_prefix = None
+ self.name = ''
+ self.marker = ''
+ self.prefix = ''
+ self.is_truncated = False
+ self.delimiter = ''
+ self.max_keys = 0
+ self.next_marker = ''
+ self.is_echoed_prefix_set = False
+
+ def startElement(self, name, attrs):
+ if name == 'Contents':
+ self.curr_entry = ListEntry()
+ elif name == 'Owner':
+ self.curr_entry.owner = Owner()
+ elif name == 'CommonPrefixes':
+ self.curr_common_prefix = CommonPrefixEntry()
+
+
+ def endElement(self, name):
+ if name == 'Contents':
+ self.entries.append(self.curr_entry)
+ elif name == 'CommonPrefixes':
+ self.common_prefixes.append(self.curr_common_prefix)
+ elif name == 'Key':
+ self.curr_entry.key = self.curr_text
+ elif name == 'LastModified':
+ self.curr_entry.last_modified = self.curr_text
+ elif name == 'ETag':
+ self.curr_entry.etag = self.curr_text
+ elif name == 'Size':
+ self.curr_entry.size = int(self.curr_text)
+ elif name == 'ID':
+ self.curr_entry.owner.id = self.curr_text
+ elif name == 'DisplayName':
+ self.curr_entry.owner.display_name = self.curr_text
+ elif name == 'StorageClass':
+ self.curr_entry.storage_class = self.curr_text
+ elif name == 'Name':
+ self.name = self.curr_text
+ elif name == 'Prefix' and self.is_echoed_prefix_set:
+ self.curr_common_prefix.prefix = self.curr_text
+ elif name == 'Prefix':
+ self.prefix = self.curr_text
+ self.is_echoed_prefix_set = True
+ elif name == 'Marker':
+ self.marker = self.curr_text
+ elif name == 'IsTruncated':
+ self.is_truncated = self.curr_text == 'true'
+ elif name == 'Delimiter':
+ self.delimiter = self.curr_text
+ elif name == 'MaxKeys':
+ self.max_keys = int(self.curr_text)
+ elif name == 'NextMarker':
+ self.next_marker = self.curr_text
+
+ self.curr_text = ''
+
+ def characters(self, content):
+ self.curr_text += content
+
+
+class ListAllMyBucketsHandler(xml.sax.ContentHandler):
+ def __init__(self):
+ self.entries = []
+ self.curr_entry = None
+ self.curr_text = ''
+
+ def startElement(self, name, attrs):
+ if name == 'Bucket':
+ self.curr_entry = Bucket()
+
+ def endElement(self, name):
+ if name == 'Name':
+ self.curr_entry.name = self.curr_text
+ elif name == 'CreationDate':
+ self.curr_entry.creation_date = self.curr_text
+ elif name == 'Bucket':
+ self.entries.append(self.curr_entry)
+
+ def characters(self, content):
+ self.curr_text = content
+
+
+class LocationHandler(xml.sax.ContentHandler):
+ def __init__(self):
+ self.location = None
+ self.state = 'init'
+
+ def startElement(self, name, attrs):
+ if self.state == 'init':
+ if name == 'LocationConstraint':
+ self.state = 'tag_location'
+ self.location = ''
+ else: self.state = 'bad'
+ else: self.state = 'bad'
+
+ def endElement(self, name):
+ if self.state == 'tag_location' and name == 'LocationConstraint':
+ self.state = 'done'
+ else: self.state = 'bad'
+
+ def characters(self, content):
+ if self.state == 'tag_location':
+ self.location += content
+
diff --git a/s3config.py b/s3config.py
new file mode 100644
index 0000000..49357ad
--- /dev/null
+++ b/s3config.py
@@ -0,0 +1,3 @@
+AWS_ACCESS_KEY_ID = 'AKIAIR53VPBXKJMXZIBA'
+AWS_SECRET_ACCESS_KEY = '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'
+BUCKET_NAME = 'i.asdf.us'
diff --git a/test.sh b/test.sh
new file mode 100644
index 0000000..8892abd
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+curl --data 'width=200&height=500&stripes=true&stripenumber=50&stripeintensity=50&color1=green&color2=silver' localhost:8999/gradient
+echo ""
+curl --data 'width=200&height=500' localhost:8999/imgrid
+echo ""
+curl --data 'url=http://i.asdf.us/im/56/6005101large_1424913577_bike.gif&breaktype=RGB_WASH&breakmode=extreme' localhost:8999/breaker