// ================================================================================================
// Script Name: autocrop.js
// ================================================================================================
// Version 2.0 9 December 2003
// Description: crops the active document after image rotation
// This version is compatible with Photoshop CS and will not run on Photoshop 7

// This script is product of Paul Jaruszewski and Roger Cavanagh
// Contact Paul at www.melor.com
// Contact Roger at www.rogercavanagh.com

// This script is provided free for personal use.
// No liability will be accepted for any consequences arising from the use of this script.
// This script may be copied IN ITS ENTIRETY to others provided and no charge is made other
// than for media. Credit to Paul and Roger must be made if this script is posted for download
// or distribution.

// Installation: copy the file autocrop.js to the ..\Presets\Scripts folder

// Globals

var chosenMethod = 0;
var ui_NoOpenDocuement = "No open document";
var hlpMsg = new Array();
var msg = "";
var hid_BinChop = 1;
msg = "Binary Search will usually be the fastest method to find edges, but may be slower than the ";
msg = msg + "Slice method with smaller amounts (1-2 degrees) of rotation. May produce an over";
msg = msg + "-crop, if the search inadvertently matches the background colour inside the picture.";
hlpMsg[hid_BinChop] = msg;
var hid_Slice = 2;
msg = "Almost as fast as binary search method, but will sometimes produce slight over-crop. This "; 
msg = msg + "can be avoided by ensuring the background colour is different from the edges (10 px) ";
msg = msg + "colour in the picture";
hlpMsg[hid_Slice] = msg;
var hid_UniPixel = 3;
msg = "This is much the slowest search method, but will only produce an error, ";
msg = msg + "if edges pixels are the same colour as the background.";
hlpMsg[hid_UniPixel] = msg;
var hid_BestResults = 4;
msg = "For best results, you should make sure the background colour does not occur near the top ";
msg = msg + "left corner of your picture. If the picture contains predominantly dark colours, ";
msg = msg + "set a white background before image rotation, and vice versa.";
hlpMsg[hid_BestResults] = msg;

// ================================================================================================
function settingDialog() { // Creates dialog window and gets user input         ===settingDialog===
// ================================================================================================

// These are functions in scope for settingDialog ()

// ------------------------------------------------------------------------------aboutBtnOnClick---
function aboutBtnOnClick() { 

	var msg = "Autocrop V2\n\n\Copyright \u00A9 2003 Paul Jaruszewski and Roger Cavanagh\n";
	msg = msg + "www.melor.com - www.rogercavanagh.com\n";
	msg = msg + "Information: http://www.rogercavanagh.com/actions/15_autocrop.htm";
	dlg.msgPnl.helpMsgSt.text = msg;

} // end about BtnOnClick 

function cancelBtnOnClick() { // -----------------------------------------------cancelBtnOnClick---

	dlg.close();
	
} // end cancelBtnOnClick 

function evalMethod() { //------------------------------------------------------------evalMethod---

	var result = 0;
	if (dlg.methodPnl.binChop.value) { result = 1; };
	if (dlg.methodPnl.slice.value) { result = 2; };
	if (dlg.methodPnl.uniPixel.value) { result = 3; };
	return result;
	
} // end function evalMethod 

function runBtnOnClick() { // -----------------------------------------------------runBtnOnClick---

	chosenMethod = evalMethod();
	dlg.close();
	
} // end runBtnOnClick 
		
function displayHelp(hlpMsgId) { // -------------------------------------------------displayHelp---

	dlg.msgPnl.helpMsgSt.text = hlpMsg[hlpMsgId];
	
} // end function 

// Standard Values

var mgn = 10;
var mgnTop = 20;
var rowH = 20;
var rBtnW = 120;
var btnW = 100;

// Method Panel Parameters                                           ---Method Panel Parameters---

var ul_MethodPnlTitle= "Method:";
var methodPnlRows = 3;
var methodPnlCols = 1;
var methodPnlH = methodPnlRows*(mgn + rowH) + mgnTop;
var methodPnlW = 350; // (methodPnlRows + 1)*mgn + rBtnW; // max number of items
var methodPnlX = mgn;
var methodPnlY = mgn;

// Control Panel Parameters                                          ---Control Panel Parameters---

var ul_ControlPnlTitle = "Control Panel:";
var ul_Run = "Run";
var ul_Cancel = "Cancel";
var ul_About = "About";

var controlPnlRows = 3;
var controlPnlCols = 1;
var controlPnlH = methodPnlH;
var controlPnlW = btnW + (controlPnlCols + 1)*mgn
var controlPnlX = methodPnlX + methodPnlW + mgn;
var controlPnlY = mgn;

// Message Panel Parameters                                          ---Message Panel Parameters---

var ul_MsgPnlTitle = "";
var msgPnlH = 140;
var msgPnlW = methodPnlW + controlPnlW + mgn;
var msgPnlX = mgn;
var msgPnlY = methodPnlY + methodPnlH + mgn;
var ul_BinarySearch = "Binary Search";
var ul_PixelSlice = "Pixel Slice";
var ul_SinglePixel = "Single Pixel";

// Dialog Window Parameters                                          ---Dialog Window Parameters---

var uiTitle = "Autocrop";
var numPnlsAcross = 2;
var numPnlsDown = 2;
var dlgX = 100;
var dlgY = 100;
var dlgH = methodPnlH + msgPnlH + (numPnlsDown + 1)*mgn;
var dlgW = methodPnlW + controlPnlW + (numPnlsAcross + 1)*mgn;
var bounds = new Array(4);
var x1 = 0, x2 = 0, y1 = 0;

// Create window and panels                                          ---Create window and panels---

bounds = {x:dlgX, y:dlgY, width:dlgW, height:dlgH};
var dlg = new Window("dialog", uiTitle, bounds);
bounds = {x:methodPnlX, y:methodPnlY, width:methodPnlW, height:methodPnlH};
dlg.methodPnl = dlg.add("panel", bounds, ul_MethodPnlTitle);
bounds = {x:controlPnlX, y:controlPnlY, width:controlPnlW, height:controlPnlH};
dlg.controlPnl = dlg.add("panel", bounds, "Control Panel:"); 

// Method Panel Elements                                                ---Method Panel Elements---

x1 = mgn;
y1 = mgnTop;
bounds = {x:x1, y:y1, width:rBtnW, height:rowH};
dlg.methodPnl.binChop = dlg.methodPnl.add("radiobutton", bounds, ul_BinarySearch);
dlg.methodPnl.binChop.value =  true;
dlg.methodPnl.binChop.onClick = function() { displayHelp(hid_BinChop); };
y1 = y1 + rowH +mgn;
bounds = {x:x1, y:y1, width:rBtnW, height:rowH};
dlg.methodPnl.slice = dlg.methodPnl.add("radiobutton", bounds, ul_PixelSlice);
dlg.methodPnl.slice.onClick = function() { displayHelp(hid_Slice); };
y1 = y1 + rowH +mgn;
bounds = {x:x1, y:y1, width:rBtnW, height:rowH};
dlg.methodPnl.uniPixel = dlg.methodPnl.add("radiobutton", bounds, ul_SinglePixel);
dlg.methodPnl.uniPixel.onClick = function() { displayHelp(hid_UniPixel); };

// Control Panel Elements                                              ---Control Panel Elements---

x1 = mgn;
y1 = mgnTop;
bounds = {x:x1, y:y1, width:btnW, height:rowH};
dlg.controlPnl.runBtn = dlg.controlPnl.add("button", bounds, ul_Run);
dlg.controlPnl.runBtn.onClick = function() {runBtnOnClick();};
y1 = y1 + rowH + mgn;
bounds = {x:x1, y:y1, width:btnW, height:rowH};
dlg.controlPnl.cancelBtn = dlg.controlPnl.add("button", bounds, ul_Cancel);
dlg.controlPnl.cancelBtn.onClick = function() {cancelBtnOnClick();};
y1 = y1 + rowH + mgn;
bounds = {x:x1, y:y1, width:btnW, height:rowH};
dlg.controlPnl.aboutBtn = dlg.controlPnl.add("button", bounds, ul_About);
dlg.controlPnl.aboutBtn.onClick = function() {aboutBtnOnClick();};

// Message Panel Elements                                              ---Message Panel Elements---

bounds = {x:msgPnlX, y:msgPnlY, width:msgPnlW, height:msgPnlH};
dlg.msgPnl = dlg.add("panel", bounds, ""); 
x1 = mgn;
y1 = mgn;
bounds = {x:x1, y:y1, width:msgPnlW - 2*mgn, height:msgPnlH - 2*mgn};
dlg.msgPnl.helpMsgSt = dlg.msgPnl.add("statictext", bounds, "", {multiline: true});
displayHelp(hid_BestResults);

return dlg;
} // end settingDialog ========================================================end settingDialog===

// ================================================================================================
function main() { //                                                                     ===main===
// ================================================================================================

// These are functions in scope for main()
	
function doCropUniPixel() {   // Single Pixel Search Routine ---------------------doCropUniPixel---

// Declare and Initialise

	var picrgb = new Array(3);
	var backrgb = new Array(3);
	var bounds = new Array(4);
	var j = 0;
	backrgb=eyeDropper(0,0); //Find color of upper left pixel
	var maxi = docRef.width;
	if (maxi > docRef.height) {
  		maxi = docRef.height;
	}
	maxi = Math.floor(maxi/2);
	
loopOne:	
	for (j=1;j < maxi; j++) {
		picrgb=eyeDropper(j,j);
		if (backrgb[0] != picrgb[0] || backrgb[1] != picrgb[1] || backrgb[2] != picrgb[2]) { // edge!
			bounds = new Array(j, j, docRef.width - j, docRef.height - j);
			docRef.crop(bounds);
			bounds = null;
			break;  // Exit for loop
		}
		purge(PurgeTarget.ALLCACHES);
	} // end for

// prep for looptwo

	var maxi = docRef.width;
	if (maxi > docRef.height) {
  		maxi = docRef.height;
	}
	maxi = Math.floor(maxi/2);

loopTwo:
	for (j=1;j < maxi; j++) {
		picrgb=eyeDropper(docRef.width - j,j);
		if (backrgb[0] != picrgb[0] || backrgb[1] != picrgb[1] || backrgb[2] != picrgb[2]) { // edge
			bounds = new Array(j, j, docRef.width - j, docRef.height - j);
			docRef.crop(bounds);
			bounds = null;
			break;  // Exit for loop
		}
		purge(PurgeTarget.ALLCACHES);
	} // end for

} // end function doCropUniPixel 

function doCropBinChop() {   // Binary Search Routine -----------------------------doCropBinChop---

// Declare and Initialise

	var picrgb = new Array(3);
	var backrgb = new Array(3);
	var bounds = new Array(4);
	var j = 0;
	var ub= 0;
	var lb = 0;
	backrgb=eyeDropper(0,0); //Find colour of upper left pixel
	var maxi = docRef.width;
	if (maxi > docRef.height) {
  		maxi = docRef.height;
	}
	ub = Math.floor(maxi/2);

loopOne:	
	while (ub - lb > 1) {
		j = Math.floor((ub - lb)/2) + lb;
    picrgb=eyeDropper(j,j);
  	if (backrgb[0] != picrgb[0] || backrgb[1] != picrgb[1] || backrgb[2] != picrgb[2]) {
			ub = j;
		} else {
			lb = j;
		} // end if
		purge(PurgeTarget.ALLCACHES);
	} // end while
	bounds = [ub , ub , docRef.width - ub , docRef.height - ub ];
  docRef.crop(bounds);
  bounds = null;

// prep for looptwo

	maxi = docRef.width;
	if (maxi > docRef.height) {
  		maxi = docRef.height;
	}
	lb = 0;	
	ub = Math.floor(maxi/2)

loopTwo:
	while (ub - lb > 1) {
		j = Math.floor((ub - lb)/2) + lb;	
    picrgb=eyeDropper(docRef.width - j,j);
  	if (backrgb[0] != picrgb[0] || backrgb[1] != picrgb[1] || backrgb[2] != picrgb[2]) {
			ub = j;
		} else {
				lb = j;
		} // end if
	purge(PurgeTarget.ALLCACHES);
	} // end while
	bounds = [ub , ub , docRef.width - ub , docRef.height - ub];
  docRef.crop(bounds);
  bounds = null;
	
} // end function doCropBinChop 

function doCropSlice() { // +10 Pixel Routine ---------------------------------------doCropSlice---

// Declare and Initialise

	var picrgb = new Array(3);
	var backrgb = new Array(3);
	var bounds = new Array(4);
	var j = 0;
	var jInc = 10;
	var maxi = docRef.width;
	if (maxi > docRef.height) {
  	maxi = docRef.height;
	}
	backrgb=eyeDropper(0,0); //Find colour of upper left pixel
	j = 1;
loopOne:	
	while (j < maxi) {
    picrgb=eyeDropper(j,j);
  	if (backrgb[0] != picrgb[0] || backrgb[1] != picrgb[1] || backrgb[2] != picrgb[2]) {
  		if (jInc == 1) {
      	bounds = [j, j, docRef.width - j, docRef.height - j];
      	docRef.crop(bounds);
      	bounds = null;
      	break loopOne;
      } else {
      	j = j - jInc;
      	if (j < 0) {
      		j = 0;
      	}
      	jInc = 1 ;
      }
    }
		purge(PurgeTarget.ALLCACHES);
		j = j + jInc;
	} // end while
	
	maxi = docRef.width;
	if (maxi > docRef.height) {
  	maxi = docRef.height;
	}
	jInc = 5;
	j = 1;
loopTwo:	
	while (j < maxi) {
    picrgb=eyeDropper(docRef.width - j,j);
  	if (backrgb[0] != picrgb[0] || backrgb[1] != picrgb[1] || backrgb[2] != picrgb[2]) {
  		if (jInc == 1) {
				bounds = [j, j, docRef.width - j, docRef.height - j];
	    	docRef.crop(bounds);
  	  	bounds = null;
      	break loopTwo;
      } else {
      	j = j - jInc;
      	if (j < 0) {
      		j = 0;
      	}
      	jInc = 1;
      }
    }
		purge(PurgeTarget.ALLCACHES);
		j = j + jInc;
	} // end while
	
} // end function doCropSlice 

function eyeDropper(x,y) { // --------------------------------------------------------eyeDropper---

// This function returns the three values for the RGB colours of any given pixel 

	var x2 = x + 1; 
	var y2 = y + 1; 
	var out = new Array(3);
	docRef.selection.select([[x,y], [x2,y], [x2,y2], [x, y2]], SelectionType.REPLACE, 0, false); 
	for(ch in list = ["Red", "Green", "Blue"]) { 
	histogram = docRef.channels[list[ch]].histogram; 
		for (i = 0; i <= 255; i++) { 
			if (histogram[i]) {
				out[ch] = i; 
				break; 
			}
		} // end for
	} // end for
  return out; 
  
} // end function eyeDropper 

// ------------------------------------------------------------------------------------------------
// Start of main() processing                                      ---Start of main() processing---
// ------------------------------------------------------------------------------------------------

	if ( documents.length <= 0 ) {
  	alert(ui_NoOpenDocument); // so we crash out
    return;
	}

	try { 
		var docRef = activeDocument;
		var settings = settingDialog(chosenMethod);
		settings.show();
		switch(chosenMethod) {
			case 1:
				doCropBinChop();
				break;
			case 2:
				doCropSlice();
				break;
			case 3:
				doCropUniPixel();
				break;
		} // End switch
	} catch (e) {
  	alert("Something's bollixed. Error name: " + e.name + ". Error message: " + e.message);
  } // end try
  
} // end function main

// ================================================================================================
// Dispatch
// ================================================================================================

main();

