unit Utilities;

{Miscellaneous utility routines used by NIH Image}

interface

   uses
        Types, Memory, QuickDraw, Packages, Devices, Menus, Events, Fonts, Scrap, TextEdit, ToolUtils, Dialogs,
        Controls, Palettes, ColorPicker, Printing, SegLoad, Processes, QuickDrawText, TextUtils, Windows,
				OSUtils, QDOffscreen, Components, QuickTimeComponents, globals;



	procedure SetDlogItem (TheDialog: DialogPtr; item, value: integer);
	procedure OutlineButton (theDialog: DialogPtr; itemNo, CornerRad: integer);
	function GetDNum (TheDialog: DialogPtr; item: integer): LongInt;
	function GetDString (TheDialog: DialogPtr; item: integer): str255;
	procedure SetDNum (TheDialog: DialogPtr; item: integer; n: LongInt);
	procedure GetWindowRect (w: WindowPtr; var wrect: rect);
	procedure SetDReal (TheDialog: DialogPtr; item: integer; n: extended; fwidth: integer);
	procedure SetDString (TheDialog: DialogPtr; item: integer; str: str255);
	procedure DrawSItem (itemnum, fontrqst, sizerqst: integer; d: dialogptr; s: str255);
	function StringToReal (str: str255): extended;
	function GetDReal (TheDialog: DialogPtr; item: integer): extended;
	procedure RealToString (Val: extended; width, fwidth: integer; var Str: Str255);
	procedure DrawReal (Val: extended; width, fwidth: integer);
	procedure DrawJReal (hloc, vloc: integer; Val: extended; fwidth: integer);
	procedure DrawLong (i: LongInt);
	function GetInt (message: str255; default: integer; var Canceled: boolean): integer;
	function GetReal (message: str255; default: extended; precision: integer; var Canceled: boolean): extended;
	function OptionKeyDown: boolean;
	function ShiftKeyDown: boolean;
	function ControlKeyDown: boolean;
	function CommandPeriod: boolean;
	function SpaceBarDown: boolean;

	procedure SysResume;
	procedure beep;
	procedure PutMessage (str: str255);
	procedure PutError (str: str255);
	procedure UnprotectLUT;
	procedure LoadLUT (table: MyCSpecArray);
	procedure SetupLutUndo;
	procedure UndoLutChange;
	procedure DisableDensitySlice;
	procedure LoadInputLUT (address: ptr);
	procedure ResetQuickCapture;
	procedure ResetScionLG3;
	procedure ResetScionAG5;
	procedure ResetScionVG5f;
	procedure ResetFrameGrabber;
	procedure CopyOffscreen (src, dst: PixMapHandle; sRect, dRect: rect);
	procedure wait (ticks: LongInt);
	function GetScrapCount: integer;
	procedure DisplayText (update: boolean);
	procedure ScreenToOffscreen (var loc: point);
	procedure OffscreenToScreen (var loc: point);
	procedure OffScreenToScreenRect (var r: rect);
	procedure UpdateScreen (MaskRect: rect);
	procedure RestoreRoi;
	procedure Undo;
	procedure CheckOnOffItem (MenuH: MenuHandle; item, fst, lst: Integer);
	procedure SetMenuItem (menuh: menuhandle; itemnum: integer; on: boolean);
	function GetFontSize (item: integer): integer;
	function MyGetPixel (h, v: LongInt): integer;
	procedure PutPixel (h, v: LongInt; value: integer);
	procedure GetLine (h, v, count: LongInt; var line: LineType);
	procedure GetColumn (h, v, count: LongInt; var data: LineType);
	procedure PutColumn (hstart, vstart, count: LongInt; var data: LineType);
	procedure PutLine (h, v, count: LongInt; var line: LineType);
	procedure Show1Value (rvalue, CalibratedValue: extended);
	procedure Show2PlotValues (x, y: extended);
	procedure Show2Values (current, total: LongInt);
	procedure DrawXDimension (x: extended; digits: integer);
	procedure DrawYDimension (y: extended; digits: integer);
	procedure DrawRGB (index: integer);
	procedure Show3Values (hloc, vloc, ivalue: LongInt);
	procedure ShowDxDy (X, Y: extended);
	procedure PutChar (c: char);
	procedure PutTab;
	procedure PutString (str: str255);
	procedure PutReal (n: extended; width, fwidth: integer);
	procedure PutLong (n: LongInt; FieldWidth: integer);
	procedure CopyResultsToBuffer (FirstCount, LastCount: integer; Headings: boolean);
	procedure ShowWatch;
	procedure ShowAnimatedWatch;
	procedure UpdatePicWindow;
	procedure DoOperation (Operation: OpType);
	procedure SaveRoi;
	procedure KillRoi;
	procedure ShowRoi;
	procedure SetupUndo;
	procedure SetupUndoFromClip;
	procedure GetLoi (var x1, y1, x2, y2: extended);
	function NotRectangular: boolean;
	function NotInBounds: boolean;
	function NoSelection: boolean;
	function NoUndo: boolean;
	procedure CloneInfo (var OldInfo, NewInfo: PicInfo);
	function NewPicWindow (name: str255; width, height: integer): boolean;
	function GetAngle (dx, dy: extended):extended;
	procedure MakeRegion;
	procedure SelectAll (visible: boolean);
	procedure EraseScreen;
	procedure RestoreScreen;
	procedure UpdateTitleBar;
	procedure Unzoom;
	procedure DrawBString (str: string);
	procedure DrawMyGrowIcon (w: WindowPtr);
	procedure PutMemoryAlert;
	function GetBigHandle (NeededSize: LongInt): handle;
	function GetImageMemory (SaveInfo: infoPtr): ptr;
	procedure UpdateAnalysisMenu;
	procedure ExtendWindowsMenu (fname: str255; size: LongInt; wptr: WindowPtr);
	procedure MakeNewWindow (name: str255);
	function long2str (num: LongInt): str255;
	procedure PutWarning;
	procedure ScaleToFit;
	procedure SetupRoiRect;
	procedure SetForegroundColor (color: integer);
	procedure SetBackgroundColor (color: integer);
	procedure GetForegroundColor (event: EventRecord);
	procedure GetBackgroundColor (event: EventRecord);
	procedure GenerateValues;
	procedure KillOperation;
	procedure ScaleImageWindow (var trect: rect);
	procedure InvertGrayLevels;
	function TooWide: boolean;
	procedure DrawTextString (str: str255; loc: point; just: integer);
	procedure IncrementCounter;
	procedure ClearResults (i: integer);
	procedure UpdateFitEllipse;
	procedure UpdateTextItems;
	procedure MakeLowerCase (var str: str255);
	function PutMessageWithCancel (str: str255): integer;
	function CurrentWindow: integer;
	procedure FindMonitors (NewScreenDepth: integer);
	function ScreenDepth: integer;
	procedure SetFColor (index: integer);
	procedure SetBColor (index: integer);
	function DoubleToReal(d:FakeDouble):extended; {68k-bug}
	procedure RealToDouble(rr: extended; var d:FakeDouble);
	function MakeStackFromWindow: boolean;
	procedure SelectSlice (i: integer);
	procedure UpdateWindowsMenuItem;
	function AddSlice (update: boolean): boolean;
	procedure AbortMacro;
	procedure TruncateString(var str: str255; length: integer);
	procedure RemovePath(var str: str255);
	procedure CloseVdig;
	

implementation


	type
		KeyPtrType = ^KeyMap;



	{procedure MacsBug (str: str255);
	inline
		$abff;}


	procedure ShowMessage (str: str255);
		var
			vloc, hloc: integer;
			tPort: GrafPtr;
			trect: rect;
			SaveGDevice: GDHandle;
	begin
		SaveGDevice := GetGDevice;
		SetGDevice(GetMainDevice);
	  InfoMessage := str;
		GetPort(tPort);
		vloc := 35;
		hloc := 4;
		SetPort(InfoWindow);
		TextFont(Geneva);
		TextSize(9);
		Setrect(trect, hloc, vloc + 15, rwidth - 10, rheight);
		TETextBox(pointer(ord(@InfoMessage) + 1), length(InfoMessage), trect, teJustLeft);
		SetPort(tPort);
		SetGDevice(SaveGDevice);
		wait(120);
	end;


	procedure SetDlogItem (TheDialog: DialogPtr; item, value: integer);
		var
			ItemType: integer;
			ItemBox: rect;
			ItemHdl: handle;
	begin
		GetDialogItem(TheDialog, item, ItemType, ItemHdl, ItemBox);
		SetControlValue(ControlHandle(ItemHdl),value)
	end;


	procedure OutlineButton (theDialog: DialogPtr; itemNo, CornerRad: integer);
  {Draws a border around a button. 16 is the normal}
  {corner radius for small buttons }
		var
			itemType: Integer;
			itemBox: Rect;
			itemHdl: Handle;
			tempPort: GrafPtr;
	begin
		GetPort(tempPort);
		SetPort(GrafPtr(theDialog));
		GetDialogItem(theDialog, itemNo, itemType, itemHdl, itemBox);
		PenSize(3, 3);
		InSetRect(itemBox, -4, -4);
		FrameRoundRect(itemBox, cornerRad, cornerRad);
		PenSize(1, 1);
		SetPort(tempPort);
	end;


	function GetDNum (TheDialog: DialogPtr; item: integer): LongInt;
		var
			ItemType: integer;
			ItemBox: rect;
			ItemHdl: handle;
			str: str255;
			n: LongInt;
	begin
		GetDialogItem(TheDialog, item, ItemType, ItemHdl, ItemBox);
		GetDialogItemText(ItemHdl, str);
		StringToNum(str, n);
		GetDNum := n;
	end;


	function GetDString (TheDialog: DialogPtr; item: integer): str255;
		var
			ItemType: integer;
			ItemBox: rect;
			ItemHdl: handle;
			str: str255;
	begin
		GetDialogItem(TheDialog, item, ItemType, ItemHdl, ItemBox);
		GetDialogItemText(ItemHdl, str);
		GetDString := str;
	end;


	procedure SetDNum (TheDialog: DialogPtr; item: integer; n: LongInt);
		var
			ItemType: integer;
			ItemBox: rect;
			ItemHdl: handle;
			str: str255;
	begin
		GetDialogItem(TheDialog, item, ItemType, ItemHdl, ItemBox);
		NumToString(n, str);
		SetDialogItemText(ItemHdl, str)
	end;


	procedure GetWindowRect (w: WindowPtr; var wrect: rect);
  {Returns global coordinates of specified window.}
	begin
		if w <> nil then
			wrect := WindowPeek(w)^.contRgn^^.rgnBBox
		else
			SetRect(wrect, 0, 0, 0, 0);
	end;


	procedure SetDReal (TheDialog: DialogPtr; item: integer; n: extended; fwidth: integer);
		var
			ItemType: integer;
			ItemBox: rect;
			ItemHdl: handle;
			str: str255;
	begin
		GetDialogItem(TheDialog, item, ItemType, ItemHdl, ItemBox);
		RealToString(n, 1, fwidth, str);
		SetDialogItemText(ItemHdl, str)
	end;

	procedure SetDString (TheDialog: DialogPtr; item: integer; str: str255);
		var
			ItemType: integer;
			ItemBox: rect;
			ItemHdl: handle;
	begin
		GetDialogItem(TheDialog, item, ItemType, ItemHdl, ItemBox);
		SetDialogItemText(ItemHdl, str)
	end;


	function GetDReal (TheDialog: DialogPtr; item: integer): extended;
		var
			str: str255;
	begin
		str := GetDString(TheDialog, item);
		GetDReal := StringToReal(str);
	end;


	procedure DrawLong (i: LongInt);
		var
			str: str255;
	begin
		NumToString(i, str);
		DrawString(str);
	end;


	procedure RealToString (Val: extended; width, fwidth: integer; var Str: Str255);
  {Does number to string conversion equivalent to write(val:width:fwidth).}
  var
  	i:integer;
	begin
		if width<1 then width:=1;
		if (fwidth<0) or (fwidth>8) then fwidth:=0;
		str:=StringOf(val:width:fwidth);
	end;


	procedure DrawReal (Val: extended; width, fwidth: integer);
  {Displays a real(or integer) number at the current location in}
  {a form equivalent to write(val:width:fwidth) }
		var
			str: str255;
	begin
		RealToString(val, width, fwidth, str);
		DrawString(str);
	end;


	procedure DrawJReal (hloc, vloc: integer; val: extended; fwidth: integer);
  {Draws right justified real number.}
		var
			str: str255;
	begin
		if (val >= 1000.0) or (val <= -1000.0) then
			fwidth := 0;
		RealToString(val, 1, fwidth, str);
		MoveTo(hloc - StringWidth(str) - 2, vloc);
		DrawString(str);
	end;


	function GetInt (message: str255; default: integer; var Canceled: boolean): integer;
		const
			NumberID = 3;
		var
			mylog: DialogPtr;
			item: integer;
			temp: LongInt;
	begin
		ParamText(message, '', '', '');
		mylog := GetNewDialog(3000, nil, pointer(-1));
		SetDNum(MyLog, NumberID, default);
		SelectdialogItemText(MyLog, NumberID, 0, 32767);
		OutlineButton(MyLog, ok, 16);
		repeat
			ModalDialog(nil, item);
		until (item = ok) or (item = cancel);
		if item = ok then begin
				Canceled := false;
				temp := GetDNum(MyLog, NumberID);
				if (temp > -MaxInt) and (temp <= MaxInt) then
					GetInt := temp
				else begin
						beep;
						GetInt := default
					end;
			end {item=ok}
		else begin
				Canceled := true;
				GetInt := default;
			end;
		DisposeDialog(mylog);
	end;


	function GetReal (message: str255; default: extended; precision: integer; var Canceled: boolean): extended;
		const
			NumberID = 3;
		var
			mylog: DialogPtr;
			item: integer;
	begin
		InitCursor;
		ParamText(message, '', '', '');
		mylog := GetNewDialog(3000, nil, pointer(-1));
		SetDReal(MyLog, NumberID, default, precision);
		SelectdialogItemText(MyLog, NumberID, 0, 32767);
		OutlineButton(MyLog, ok, 16);
		repeat
			ModalDialog(nil, item);
		until (item = ok) or (item = cancel);
		if item = ok then begin
				GetReal := GetDReal(MyLog, NumberID);
				Canceled := false;
			end
		else begin
				GetReal := default;
				Canceled := true;
			end;
		DisposeDialog(mylog);
	end;


	function OptionKeyDown: boolean;
		var
			KeyPtr: KeyPtrType;
			keys: array[0..3] of LongInt;
	begin
		KeyPtr := KeyPtrType(@keys);
		GetKeys(KeyPtr^);
		OptionKeyDown := (BAND(keys[1], 4)) <> 0;
	end;


	function ShiftKeyDown: boolean;
		var
			KeyPtr: KeyPtrType;
			keys: array[0..3] of LongInt;
	begin
		KeyPtr := KeyPtrType(@keys);
		GetKeys(KeyPtr^);
		ShiftKeyDown := (BAND(keys[1], 1)) <> 0;
	end;


	function ControlKeyDown: boolean;
		type
			KeyPtrType = ^KeyMap;
		var
			KeyPtr: KeyPtrType;
			keys: array[0..3] of LongInt;
	begin
		KeyPtr := KeyPtrType(@keys);
		GetKeys(KeyPtr^);
		ControlKeyDown := (BAND(keys[1], 8)) <> 0;
	end;


	function CommandPeriod: boolean;
		type
			KeyPtrType = ^KeyMap;
		var
			KeyPtr: KeyPtrType;
			keys: array[0..3] of LongInt;
	begin
		KeyPtr := KeyPtrType(@keys);
		GetKeys(KeyPtr^);
		CommandPeriod := (BAND(keys[1], $808000)) = $808000;
	end;


	function SpaceBarDown: boolean;
		var
			KeyPtr: KeyPtrType;
			keys: array[0..3] of LongInt;
	begin
		KeyPtr := KeyPtrType(@keys);
		GetKeys(KeyPtr^);
		SpaceBarDown := (BAND(keys[1], 512)) <> 0;
	end;


	procedure DrawSItem (itemnum, fontrqst, sizerqst: integer; d: dialogptr; s: str255);
 {Draw a string item in a dialog box.}
		var
			r: rect;
			iType: integer;
			ignore: handle;
	begin
		GetDialogItem(d, ItemNum, iType, ignore, r);
		TextFont(fontrqst);
		TextSize(sizerqst);
		TETextBox(pointer(ord(@s) + 1), length(s), r, TEJustRight);
	end;


	procedure SysResume;
	begin
		FlushEvents(EveryEvent, 0);
		ExitToShell;
	end;


	procedure beep;
	{Sets the current gdevice to the screen because SysBeep flashes
  the menu bar if the sound level is zero and this is reported to sometimes
	cause a crash on older Macs when using an offscreen gdevice.} 
		var
			SaveGDevice: GDHandle;
	begin
		SaveGDevice := GetGDevice;
		SetGDevice(GetMainDevice);
		SysBeep(1);
		SetGDevice(SaveGDevice);
	end;


	procedure PutMessage (str: str255);
		var
			ignore: integer;
			SaveGDevice: GDHandle;
	begin
		SaveGDevice := GetGDevice;
		SetGDevice(GetMainDevice);
		InitCursor;
		ParamText(str, '', '', '');
		Ignore := Alert(300, nil);
		SetGDevice(SaveGDevice);
	end;
	

	procedure PutError (str: str255);
		var
			ignore: integer;
			SaveGDevice: GDHandle;
	begin
		SaveGDevice := GetGDevice;
		SetGDevice(GetMainDevice);
		InitCursor;
		ParamText(str, '', '', '');
		Ignore := Alert(310, nil);
		SetGDevice(SaveGDevice);
	end;


	function GetFontSize (item: integer): integer;
		var
			TempSize: integer;
			Canceled: boolean;
	begin
		case item of
			1: 
				GetFontSize := 9;
			2: 
				GetFontSize := 10;
			3: 
				GetFontSize := 12;
			4: 
				GetFontSize := 14;
			5: 
				GetFontSize := 18;
			6: 
				GetFontSize := 24;
			7: 
				GetFontSize := 36;
			8: 
				GetFontSize := 48;
			9: 
				GetFontSize := 56;
			10: 
				GetFontSize := 72;
			12:  begin
					TempSize := GetInt('Font Size:', CurrentSize, Canceled);
					if TempSize < 1 then
						TempSize := 1;
					if TempSize > 1000 then
						TempSize := 1000;
					if not canceled then
						GetFontSize := TempSize
					else
						GetFontSize := CurrentSize;
				end;
		end;
	end;


	procedure SetMenuItem (menuh: menuhandle; itemnum: integer; on: boolean);
{Enable or disable menuh's itemnum. }
	begin
		if on then
			EnableItem(menuh, itemnum)
		else
			DisableItem(menuh, itemnum);
		if ItemNum = 0 then
			DrawMenuBar;
	end;


	procedure CheckOnOffItem (MenuH: MenuHandle; item, fst, lst: Integer);
		var
			i: integer;
	begin
		for i := fst to lst do
			if i = item then
				CheckItem(MenuH, i, true)
			else
				CheckItem(MenuH, i, false);
	end;


	procedure UpdateTextItems;
		var
			size, i, MenuItem, FontID, item: integer;
			FontName: str255;
			FontFound, FoundIt: boolean;
			str: str255;
	begin
		FontFound := false;
		for item := 1 to NumFontItems do begin
				GetMenuItemText(FontMenuH, Item, FontName);
				GetFNum(FontName, FontID);
				if FontID = CurrentFontID then begin
						FontFound := true;
						CheckItem(FontMenuH, Item, True)
					end
				else
					CheckItem(FontMenuH, Item, false);
			end;
		if not FontFound then begin
				FoundIt := False;
				Item := 1;
				repeat
					GetMenuItemText(FontMenuH, Item, FontName);
					GetFNum(FontName, FontID);
					if FontID = Geneva then begin
							CheckItem(FontMenuH, Item, True);
							CurrentFontID := FontID;
							FoundIt := true;
						end;
					Item := Item + 1;
				until (Item > NumFontItems) or FoundIt;
			end;

		for i := 1 to 10 do begin
				size := GetFontSize(i);
				if RealFont(CurrentFontID, size) then
					SetItemStyle(SizeMenuH, i, [outline])
				else
					SetItemStyle(SizeMenuH, i, [])
			end;
		NumToString(CurrentSize, str);
		str := concat('Other[', str, ']É');
		SetMenuItemText(SizeMenuH, 12, str);

		for i := TxPlain to TxShadow do
			CheckItem(StyleMenuH, i, false);
		if CurrentStyle = [] then
			CheckItem(StyleMenuH, TxPlain, true)
		else begin
				if Bold in CurrentStyle then
					CheckItem(StyleMenuH, TxBold, true);
				if Italic in CurrentStyle then
					CheckItem(StyleMenuH, TxItalic, true);
				if Underline in CurrentStyle then
					CheckItem(StyleMenuH, TxUnderline, true);
				if Outline in CurrentStyle then
					CheckItem(StyleMenuH, TxOutline, true);
				if Shadow in CurrentStyle then
					CheckItem(StyleMenuH, Txshadow, true);
			end;

		case CurrentSize of
			9: 
				MenuItem := 1;
			10: 
				MenuItem := 2;
			12: 
				MenuItem := 3;
			14: 
				MenuItem := 4;
			18: 
				MenuItem := 5;
			24: 
				MenuItem := 6;
			36: 
				MenuItem := 7;
			48: 
				MenuItem := 8;
			56: 
				MenuItem := 9;
			72: 
				MenuItem := 10;
			otherwise
				MenuItem := 12;
		end;
		CheckOnOffItem(SizeMenuH, MenuItem, 1, 12);

		case TextJust of
			teJustLeft: 
				MenuItem := LeftItem;
			teJustCenter: 
				MenuItem := CenterItem;
			teJustRight: 
				MenuItem := RightItem;
		end;
		CheckOnOffItem(StyleMenuH, MenuItem, LeftItem, RightItem);

		if TextBack = NoBack then
			MenuItem := NoBackgroundItem
		else
			MenuItem := WithBackgroundItem;
		CheckOnOffItem(StyleMenuH, MenuItem, NoBackgroundItem, WithBackgroundItem);
	end;


	procedure LoadLUT (table: MyCSpecArray);
		var
			i, entry, screen: integer;
			cPtr: ^cSpecArray;
			SaveDevice: GDHandle;
	begin
		if nExtraColors > 0 then begin
				entry := FirstExtraColorsEntry;
				for i := 1 to nExtraColors do begin
						table[entry].rgb := ExtraColors[i];
						entry := entry + 1;
					end;
			end;
		if HighLightMode then begin
				table[1].rgb := Highlight1;
				table[254].rgb := Highlight254;
			end;
		for i := 1 to 254 do {Work around needed for 32-bit QuickDraw}
			with table[i].rgb do
				if (red = 0) and (green = 0) and (blue = 0) then begin
						red := 256;
						green := 256;
						blue := 256;
					end;
		cPtr := @table[1];
		if ScreenDepth = 8 then begin
			SaveDevice := GetGDevice;
			for screen := 1 to nMonitors do begin
					SetGDevice(Monitors[screen]);
					for i := 1 to 254 do begin
							ProtectEntry(i, false);
							ReserveEntry(i, false);
						end;
					SetEntries(1, 253, cPtr^);
				end;
			SetGDevice(SaveDevice);
		end;
		table[0].rgb := WhiteRGB;
		table[255].rgb := BlackRGB;
		BlockMove(@table, @osGDevice^^.gdPMap^^.pmTable^^.ctTable, SizeOf(table));
		with osGDevice^^.gdPMap^^.pmTable^^ do
			if ScreenDepth = 8 then
				ctSeed := ScreenPixMap^^.pmTable^^.ctSeed
			else
				ctSeed := GetCtSeed;
	end;


	procedure SetupLutUndo;
	begin
		with info^ do begin
				UndoInfo^.RedLut := RedLut;
				UndoInfo^.GreenLut := GreenLut;
				UndoInfo^.BlueLut := BlueLut;
				UndoInfo^.nColors := nColors;
				UndoInfo^.ColorStart := ColorStart;
				UndoInfo^.ColorEnd := ColorEnd;
				UndoInfo^.FillColor1 := FillColor1;
				UndoInfo^.FillColor2 := FillColor2;
				UndoInfo^.LutMode := LutMode;
				UndoInfo^.ColorTable := ColorTable;
				UndoInfo^.IdentityFunction := IdentityFunction;
				UndoInfo^.cTable := cTable;
				WhatToUndo := UndoLUT;
			end;
	end;


	procedure UndoLutChange;
	begin
		with info^ do begin
				RedLut := UndoInfo^.RedLut;
				GreenLut := UndoInfo^.GreenLut;
				BlueLut := UndoInfo^.BlueLut;
				nColors := UndoInfo^.nColors;
				ColorStart := UndoInfo^.ColorStart;
				ColorEnd := UndoInfo^.ColorEnd;
				FillColor1 := UndoInfo^.FillColor1;
				FillColor2 := UndoInfo^.FillColor2;
				LutMode := UndoInfo^.LutMode;
				LutMode := UndoInfo^.LutMode;
				ColorTable := UndoInfo^.ColorTable;
				cTable := UndoInfo^.cTable;
				LoadLut(cTable);
				Thresholding := false;
				WhatToUndo := NothingToUndo;
			end;
	end;


	procedure UpdatePicWindow;
		var
			tPort: GrafPtr;
			SaveGDevice: GDHandle;
	begin
		if (info <> NoInfo) and (info^.wptr <> nil) then
			with Info^ do begin
					SaveGDevice := GetGDevice;
					SetGDevice(GetMainDevice);
					getPort(tPort);
					SetPort(wptr);
					SetFColor(BlackIndex);
					SetBColor(WhiteIndex);
					CopyBits(BitMapHandle(osPort^.portPixMap)^^, BitMapHandle(CGrafPtr(wptr)^.PortPixMap)^^, SrcRect, wrect, gCopyMode, nil);
					SetPort(tPort);
					SetGDevice(SaveGDevice);
					RoiUpdateTime := 0;
				end;
	end;


	procedure DisableDensitySlice;
		var
			tPort: GrafPtr;
	begin
		if DensitySlicing then begin
				DensitySlicing := false;
				UndoLutChange;
				if ScreenDepth <> 8 then begin
						UpdatePicWindow;
						GetPort(tPort);
						SetPort(LUTWindow);
						InvalRect(LutWindow^.PortRect);
						SetPort(tPort);
					end;
			end;
	end;


	procedure LoadInputLUT (address: ptr);
		type
			ilutType = packed array[0..1023] of byte;
			ilutPtr = ^ilutType;
		var
			ilut: ilutPtr;
			i: integer;
	begin
		ilut := ilutPtr(address);
		if InvertVideo then begin
				for i := 0 to 255 do
					ilut^[i * 4] := i;
				ilut^[0] := 1;
				ilut^[255 * 4] := 254
			end
		else begin
				for i := 0 to 255 do
					ilut^[i * 4] := 255 - i;
				ilut^[0] := 254;
				ilut^[255 * 4] := 1
			end;
	end;


	procedure ResetQuickCapture;
		const
			ilutOffset = $90000;
	begin
		ControlReg^ := 1; {reset}
		while BitAnd(ControlReg^, $80) = $80 do
			;
		ChannelReg^ := VideoChannel * 64;
		while BitAnd(ControlReg^, $80) = $80 do
			;
		LoadInputLUT(Ptr(fgSlotBase + ilutOffset));
	end;


	procedure ResetScionLG3;
		const
			ilutOffset = $80000;
		var
			SyncChannel, t: integer;
	begin
		ControlReg^ := 0;
		BufferReg^ := 0;
		if SyncMode = SeparateSync then
			SyncChannel := 3
		else
			SyncChannel := VideoChannel;
		t := band(bsl(VideoChannel, 4), bsl(SyncChannel, 6));
		ChannelReg^ := bor(LG3DataOut, bor(bsl(VideoChannel, 4), bsl(SyncChannel, 6)));
		DacHighReg^ := DacHigh;
		DacLowReg^ := DacLow;
		DacAReg^ := LG3DacA;
		DacBReg^ := LG3DacB;
		LoadInputLUT(Ptr(fgSlotBase + ilutOffset));
	end;


	procedure ResetScionAG5;
		const
			ilutOffset = $E0000;
		var
			SyncChannel: integer;
	begin
		ControlReg^ := 0;
		if SyncMode = SeparateSync then
			SyncChannel := 3
		else
			SyncChannel := VideoChannel;
		ChannelReg^ := bor(ord(AG5BufferMode), bor(bsl(VideoChannel, 4), bsl(SyncChannel, 6)));
		DacHighReg^ := DacHigh;
		DacLowReg^ := DacLow;
		LoadInputLUT(Ptr(fgSlotBase + ilutOffset));
	end;


	procedure ResetScionVG5f;
		const
			ilutOffset = $80000;
		var
			SyncChannel, t: integer;
	begin
		ControlReg^ := 0;
		if SyncMode = SeparateSync then
			SyncChannel := 3
		else
			SyncChannel := VideoChannel;
		t := band(bsl(VideoChannel, 4), bsl(SyncChannel, 6));
		ChannelReg^ := bor(bsl(VideoChannel, 4), bsl(SyncChannel, 6));
		DacHighReg^ := DacHigh;
		DacLowReg^ := DacLow;
		LoadInputLUT(Ptr(fgSlotBase + ilutOffset));
	end;


	procedure ResetFrameGrabber;
	begin
		case FrameGrabber of
			QuickCapture: 
				ResetQuickCapture;
			ScionLG3: 
				ResetScionLG3;
			ScionAG5: 
				ResetScionAG5;
			ScionVG5f:
				ResetScionVG5f;
			otherwise
				;
		end;
	end;


	procedure CopyOffscreen (src, dst: PixMapHandle; sRect, dRect: rect);
		var
			SaveGDevice: GDHandle;
	begin
		SaveGDevice := GetGDevice;
		SetGDevice(osGDevice);
		pmForeColor(BlackIndex);
		pmBackColor(WhiteIndex);
		CopyBits(BitMapHandle(fgPixMap)^^, BitMapHandle(dst)^^, sRect, dRect, DitherCopy, nil);
		pmForeColor(ForegroundIndex);
		pmBackColor(BackgroundIndex);
		SetGDevice(SaveGDevice);
	end;


	procedure wait (ticks: LongInt);
		var
			SaveTicks: LongInt;
	begin
		SaveTicks := TickCount + ticks;
		repeat
		until TickCount > SaveTicks;
	end;


	function GetScrapCount: integer;
		var
			ScrapInfo: ScrapStuffPtr;
	begin
		ScrapInfo := InfoScrap;
		GetScrapCount := ScrapInfo^.ScrapCount;
	end;


	procedure DisplayText (update: boolean);
		var
			tPort: GrafPtr;
			i, hstart, width, ff: integer;
			MaskRect: rect;
			p1, p2: point;
			SaveGDevice: GDHandle;
	begin
		if (info = NoInfo) or (not IsInsertionPoint) then
			exit(DisplayText);
		if update then
			Undo;
		SaveGDevice := GetGDevice;
		SetGDevice(osGDevice);
		GetPort(tPort);
		SetPort(GrafPtr(Info^.osPort));
		pmForeColor(ForegroundIndex);
		pmBackColor(BackgroundIndex);
		TextFont(CurrentFontID);
		TextFace(CurrentStyle);
		TextSize(CurrentSize);
		if TextBack = NoBack then
			TextMode(SrcOr)
		else
			TextMode(SrcCopy);
		width := StringWidth(TextStr);
		case TextJust of
			teJustLeft: 
				hstart := TextStart.h;
			teJustCenter: 
				hstart := TextStart.h - width div 2;
			teJustRight: 
				hstart := TextStart.h - width;
		end;
		if hstart < 0 then
			hstart := 0;
		MoveTo(hstart, TextStart.v);
		DrawString(TextStr);
		GetPen(InsertionPoint);
		ff := CurrentSize * 2;
		p1.h := hstart - ff;
		p1.v := TextStart.v - CurrentSize;
		p2.h := TextStart.h + width + ff;
		p2.v := TextStart.v + CurrentSize div 3;
		Pt2Rect(p1, p2, MaskRect);
		UpdateScreen(MaskRect);
		SetPort(tPort);
		SetGDevice(SaveGDevice);
		Info^.changes := true;
	end;


	procedure OffScreenToScreenRect (var r: rect);
		var
			p1, p2: point;
	begin
		with r do begin
				p1.h := left;
				p1.v := top;
				p2.h := right;
				p2.v := bottom;
				OffScreenToScreen(p1);
				OffScreenToScreen(p2);
				Pt2Rect(p1, p2, r);
			end;
	end;


	procedure ScreenToOffscreen (var loc: point);
	begin
		with loc, Info^ do begin
				h := SrcRect.left + trunc(h / magnification);
				v := SrcRect.top + trunc(v / magnification);
			end;
	end;


	procedure OffscreenToScreen (var loc: point);
	begin
		with loc, Info^ do begin
				h := trunc((h - SrcRect.left) * magnification);
				v := trunc((v - SrcRect.top) * magnification);
			end;
	end;



	procedure UpdateScreen (MaskRect: rect);
 {Refreshes the portion of the screen defined by}
  {MaskRect, where MaskRect is defined in offscreen coordinates.}
		var
			tPort: GrafPtr;
			imag: integer;
			SaveGDevice: GDHandle;
	begin
		OffScreenToScreenRect(MaskRect);
		with Info^ do
			if info <> NoInfo then begin
					SaveGDevice := GetGDevice;
					SetGDevice(GetMainDevice);
					getPort(tPort);
					SetPort(wptr);
					SetFColor(BlackIndex);
					SetBColor(WhiteIndex);
					imag := trunc(magnification);
					InsetRect(MaskRect, -imag * 2 * LineWidth, -imag * 2 * LineWidth);
					InsetRect(MaskRect, 0, 0);
					RectRgn(MaskRgn, MaskRect);
					CopyBits(BitMapHandle(osPort^.PortPixMap)^^, BitMapHandle(CGrafPtr(wptr)^.PortPixMap)^^, SrcRect, wrect, gCopyMode, MaskRgn);
					SetPort(tPort);
					SetGDevice(SaveGDevice);
				end;
	end;


	procedure RestoreRoi;
	begin
		with Info^ do begin
				SetupUndo;
				if RoiShowing then
					UpdateScreen(RoiRect);
				roiType := NoInfo^.roiType;
				RoiRect := NoInfo^.RoiRect;
				CopyRgn(NoInfo^.roiRgn, roiRgn);
				LX1 := NoInfo^.LX1;
				LY1 := NoInfo^.LY1;
				LX2 := NoInfo^.LX2;
				LY2 := NoInfo^.LY2;
				LAngle := NoInfo^.LAngle;
				RoiShowing := true;
				measuring := false;
			end;
	end;


	procedure Undo;
		var
			SrcPtr: ptr;
			line: integer;
	begin
		if info^.PixMapSize <> CurrentUndoSize then
			exit(Undo);
		if UndoFromClip then begin
				if info^.PixMapSize > ClipBufSize then
					exit(Undo);
				SrcPtr := ClipBuf;
			end
		else
			SrcPtr := UndoBuf;
		with info^ do
			BlockMove(SrcPtr, PicBaseAddr, PixMapSize);
		if UndoFromClip and RestoreUndoBuf then
			with info^ do
				BlockMove(SrcPtr, UndoBuf, PixMapSize);
		if RedoSelection then
			RestoreRoi;
	end;


	function MyGetPixel (h, v: LongInt): integer;
	begin
		MyGetPixel := BackgroundIndex;
		with Info^ do
			if h >= 0 then
				if v >= 0 then
					if h < PixelsPerLine then
						if v < nlines then
							MyGetPixel := ImageP(PicBaseAddr)^[v * BytesPerRow + h];
               {MyGetPixel := band(ptr(Ord4(PicBaseAddr) + v * BytesPerRow + h)^, 255);}
	end;


	procedure PutPixel (h, v: LongInt; value: integer);
		var
			addr: Ptr;
	begin
		with Info^ do
			if h >= 0 then
				if v >= 0 then
					if h < PixelsPerLine then
						if v < nlines then begin
								addr := Ptr(Ord4(PicBaseAddr) + v * BytesPerRow + h);
								addr^ := value;
							end;
	end;


	procedure GetLine (h, v, count: LongInt; var line: LineType);
		var
			offset: LongInt;
			p: ptr;
			i: integer;
	begin
		if count > MaxLine then
			count := MaxLine;
		with Info^ do begin
				if (h < 0) or (v < 0) or ((h + count) > PixelsPerLine) or (v >= nlines) then begin
						for i := 0 to count - 1 do
							line[i] := MyGetPixel(h + i, v);
						exit(GetLine);
					end;
				offset := v * BytesPerRow + h;
				p := ptr(ord4(PicBaseAddr) + offset);
				BlockMove(p, @line, count);
			end;
	end;


	procedure GetColumn (h, v, count: LongInt; var data: LineType);
		var
			col, pic, bpr: LongInt;
			i: integer;
	begin
		if count > MaxLine then
			count := MaxLine;
		with Info^ do begin
				if (h < 0) or (v < 0) or (h >= PixelsPerLine) or ((v + count) > nlines) then begin
						for i := 0 to count - 1 do
							data[i] := MyGetPixel(h, v + i);
						exit(GetColumn);
					end;
				col := Ord4(@data);
				bpr := BytesPerRow;
				pic := Ord4(PicBaseAddr) + v * bpr + h;
				while count > 0 do begin
						Ptr(col)^ := Ptr(pic)^;
						pic := pic + bpr;
						col := col + 1;
						count := count - 1;
					end;
			end;
	end;


	procedure PutColumn (hstart, vstart, count: LongInt; var data: LineType);
		var
			col, pic, bpr: LongInt;
	begin
		col := Ord4(@data);
		with Info^ do begin
				bpr := BytesPerRow;
				if count > 0 then
					if hstart >= 0 then
						if vstart >= 0 then
							if hstart < PixelsPerLine then begin
									if vstart > nlines - count then
										count := nlines - vstart;
									pic := Ord4(PicBaseAddr) + vstart * bpr + hstart;
									while count > 0 do begin
											Ptr(pic)^ := Ptr(col)^;
											pic := pic + bpr;
											col := col + 1;
											count := count - 1;
										end;
								end;
			end;
	end;


	procedure PutLine (h, v, count: LongInt; var line: LineType);
		var
			offset: LongInt;
			p: ptr;
	begin
		with Info^ do begin
				if (h < 0) or (v < 0) or (v >= nlines) then
					exit(PutLine);
				if (h + count) > PixelsPerLine then
					count := PixelsPerLine - h;
				offset := v * BytesPerRow + h;
				p := ptr(ord4(PicBaseAddr) + offset);
				BlocKMove(@line, p, count);
			end;
	end;


	procedure Show1Value (rvalue, CalibratedValue: extended);
		var
			tPort: GrafPtr;
			hstart, vstart, ivalue: integer;
	begin
		hstart := InfoHStart;
		vstart := InfoVStart;
		GetPort(tPort);
		SetPort(InfoWindow);
		TextSize(9);
		TextFont(Monaco);
		TextMode(SrcCopy);
		MoveTo(xValueLoc, vstart);
		if CalibratedValue <> NoValue then begin
				DrawReal(CalibratedValue, 5, 2);
				DrawString(' (');
				DrawReal(rvalue, 3, 0);
				DrawString(')');
			end
		else
			DrawReal(rvalue, 6, 2);
		DrawString('    ');
		SetPort(tPort);
	end;


	procedure Show2PlotValues (x, y: extended);
		var
			tPort: GrafPtr;
			hstart, vstart, ivalue: integer;
	begin
		with info^ do begin
				hstart := InfoHStart;
				vstart := InfoVStart;
				GetPort(tPort);
				SetPort(InfoWindow);
				TextSize(9);
				TextFont(Monaco);
				TextMode(SrcCopy);
				MoveTo(xValueLoc, vstart);
				DrawXDimension(round(x), 0);
				MoveTo(yValueLoc, vstart + 10);
				DrawReal(y, 6, 2);
				SetPort(tPort);
			end;
	end;


	procedure Show2Values (current, total: LongInt);
		var
			tPort: GrafPtr;
			hstart, vstart, ivalue: integer;
	begin
		hstart := InfoHStart;
		vstart := InfoVStart;
		GetPort(tPort);
		SetPort(InfoWindow);
		TextSize(9);
		TextFont(Monaco);
		TextMode(SrcCopy);
		MoveTo(xValueLoc, vstart);
		DrawLong(current);
		DrawString('     ');
		MoveTo(yValueLoc, vstart + 10);
		DrawLong(total);
		DrawString('     ');
		SetPort(tPort);
	end;


	procedure DrawXDimension (x: extended; digits: integer);
	begin
		with info^ do begin
				if SpatiallyCalibrated then begin
						DrawReal(x / xScale, 5, 2);
						DrawChar(xUnit[1]);
						DrawChar(xUnit[2]);
						DrawString(' (');
						DrawReal(x, 3, digits);
						DrawString(')')
					end
				else
					DrawReal(x, 1, digits);
				DrawString('      ');
			end;
	end;


	procedure DrawYDimension (y: extended; digits: integer);
	begin
		with info^ do begin
				if SpatiallyCalibrated then begin
						DrawReal(y / yScale, 5, 2);
						DrawChar(xUnit[1]);
						DrawChar(xUnit[2]);
						DrawString(' (');
						DrawReal(y, 3, digits);
						DrawString(')')
					end
				else
					DrawReal(y, 1, digits);
				DrawString('      ');
			end;
	end;


	procedure DrawRGB (index: integer);
		var
			rStr, gStr, bStr: str255;
			TempRGB: rgbColor;
			i, entry: integer;

		procedure Convert (n: integer; var str: str255);
			var
				i: integer;
		begin
			RealToString(n, 3, 0, str);
			for i := 1 to 3 do
				if str[i] = ' ' then
					str[i] := '0';
		end;

	begin
		if ScreenDepth = 8 then
			TempRGB := cScreenPort^.portPixMap^^.pmTable^^.ctTable[index].rgb
		else
			TempRGB := info^.cTable[index].rgb;
		with TempRGB do begin
				Convert(band(bsr(red, 8), 255), rStr);
				Convert(band(bsr(green, 8), 255), gStr);
				Convert(band(bsr(blue, 8), 255), bStr);
				DrawString(concat(rStr, ' ', gStr, ' ', bStr));
			end;
	end;


	procedure Show3Values (hloc, vloc, ivalue: LongInt);
		var
			tPort: GrafPtr;
			hstart, vstart: integer;
	begin
		with info^ do begin
				hstart := InfoHStart;
				vstart := InfoVStart;
				GetPort(tPort);
				SetPort(InfoWindow);
				TextSize(9);
				TextFont(Monaco);
				TextMode(SrcCopy);
				if hloc < 0 then
					hloc := -hloc;
				MoveTo(xValueLoc, vstart);
				DrawXDimension(hloc, 0);
				if InvertYCoordinates and (ivalue >= 0) then
					vloc := PicRect.bottom - vloc - 1;
				if vloc < 0 then
					vloc := -vloc;
				MoveTo(yValueLoc, vstart + 10);
				DrawYDimension(vloc, 0);
				DrawString('    ');
				if ivalue >= 0 then begin
						MoveTo(zValueLoc, vstart + 20);
						if (fit <> uncalibrated) or (CurrentTool = PickerTool) then begin
								if CurrentTool = PickerTool then
									DrawRGB(ivalue)
								else
									DrawReal(cvalue[ivalue], 5, precision);
								DrawString(' (');
								DrawLong(ivalue);
								DrawString(')');
							end
						else
							DrawLong(ivalue);
					end;
				DrawString('    ');
				SetPort(tPort);
			end;
	end;


	procedure ShowDxDy (X, Y: extended);
		var
			tPort: GrafPtr;
			hstart, vstart, ivalue: integer;
	begin
		with info^ do begin
				hstart := InfoHStart;
				vstart := InfoVStart;
				GetPort(tPort);
				SetPort(InfoWindow);
				TextSize(9);
				TextFont(Monaco);
				TextMode(SrcCopy);
				MoveTo(xValueLoc, vstart);
				DrawXDimension(x, 2);
				MoveTo(yValueLoc, vstart + 10);
				DrawYDimension(y, 2);
				MoveTo(zValueLoc, vstart + 20);
				if SpatiallyCalibrated then begin
						DrawReal(sqrt(sqr(x / xScale) + sqr(y / yScale)), 5, 2);
						DrawChar(xUnit[1]);
						DrawChar(xUnit[2]);
						DrawString(' (');
						DrawReal(sqrt(sqr(x) + sqr(y)), 1, 2);
						DrawString(')')
					end
				else
					DrawReal(sqrt(sqr(x) + sqr(y)), 1, 2);
				DrawString('    ');
				SetPort(tPort);
			end;
	end;


	procedure PutChar (c: char);
	begin
		if TextBufSize < MaxTextBufSize then begin
				TextBufSize := TextBufSize + 1;
				TextBufP^[TextBufSize] := c;
				if c = cr then begin
						TextBufColumn := 0;
						TextBufLineCount := TextBufLineCount + 1
					end
				else
					TextBufColumn := TextBufColumn + 1;
			end;
	end;


	procedure PutTab;
	begin
		if not printing then
			PutChar(tab)
	end;


	procedure PutString (str: str255);
		var
			i: integer;
	begin
		for i := 1 to length(str) do begin
				if TextBufSize < MaxTextBufSize then
					TextBufSize := TextBufSize + 1;
				TextBufP^[TextBufSize] := str[i];
				TextBufColumn := TextBufColumn + 1;
			end;
	end;


	procedure PutFString (str: str255; FieldWidth: integer);
		var
			LeadingSpaces: integer;
	begin
		LeadingSpaces := FieldWidth - length(str);
		if LeadingSpaces > 0 then
			str := concat(copy('            ', 1, LeadingSpaces), str);
		PutString(str);
	end;


	procedure PutReal (n: extended; width, fwidth: integer);
		var
			str: str255;
	begin
		RealToString(n, width, fwidth, str);
		PutString(str);
	end;


	procedure PutLong (n: LongInt; FieldWidth: integer);
		var
			str: str255;
			LeadingSpaces: integer;
	begin
		NumToString(n, str);
		LeadingSpaces := FieldWidth - length(str);
		if LeadingSpaces > 0 then
			str := concat(copy('            ', 1, LeadingSpaces), str);
		PutString(str);
	end;


	procedure CopyResultsToBuffer (FirstCount, LastCount: integer; Headings: boolean);
		var
			i, column, fwidth: integer;
			m: MeasurementTypes;

		procedure PutSequenceNumber;
		begin
			PutLong(i, 4);
			PutChar('.');
			PutTab;
		end;

		procedure PutUnits;
		begin
			if info^.SpatiallyCalibrated then begin
					PutString('  (');
					DrawChar(info^.xUnit[1]);
					DrawChar(info^.xUnit[2]);
					PutString(')')
				end
			else
				PutString('(Pixels)');
			PutChar(cr);
			PutChar(cr);
		end;

		procedure PutTabDelimeter;
		begin
			Column := Column + 1;
			if Column <> nListColumns then
				PutTab;
		end;

	begin
		if mCount < 1 then begin
				TextBufSize := 0;
				TextBufLineCount := 0;
				exit(CopyResultsToBuffer);
			end;
		ShowWatch;
		Headings := Headings or OptionKeyWasDown;
		TextBufSize := 0;
		TextBufColumn := 0;
		TextBufLineCount := 0;
		nListColumns := 0;
		for m := AreaM to StdDevM do
			if m in Measurements then
				nListColumns := nListColumns + 1;
		if (xyLocM in measurements) or (nPoints > 0) then
			nListColumns := nListColumns + 2;
		if ModeM in measurements then
			nListColumns := nListColumns + 1;
		if (LengthM in measurements) or (nLengths > 0) then
			nListColumns := nListColumns + 1;
		if MajorAxisM in measurements then
			nListColumns := nListColumns + 1;
		if MinorAxisM in measurements then
			nListColumns := nListColumns + 1;
		if (AngleM in measurements) or (nAngles > 0) then
			nListColumns := nListColumns + 1;
		if IntDenM in measurements then
			nListColumns := nListColumns + 2;
		if MinMaxM in measurements then
			nListColumns := nListColumns + 2;
		if User1M in measurements then
			nListColumns := nListColumns + 1;
		if User2M in measurements then
			nListColumns := nListColumns + 1;
		with info^ do begin
				fwidth := FieldWidth;
				if Headings and (FirstCount = 1) then begin
						PutFString(' ', 5);
						PutTabDelimeter;
						if AreaM in measurements then begin
								PutFString('Area', fwidth);
								PutTabDelimeter;
							end;
						if MeanM in measurements then begin
								PutFString('Mean', fwidth);
								PutTabDelimeter;
							end;
						if StdDevM in measurements then begin
								PutFString('S.D.', fwidth);
								PutTabDelimeter;
							end;
						if (xyLocM in measurements) or (nPoints > 0) then begin
								PutFString('X', fwidth);
								PutTabDelimeter;
								PutFString('Y', fwidth);
								PutTabDelimeter;
							end;
						if ModeM in measurements then begin
								PutFString('Mode', fwidth);
								PutTabDelimeter;
							end;
						if (LengthM in measurements) or (nLengths > 0) then begin
								PutFString('Length', fwidth);
								PutTabDelimeter;
							end;
						if MajorAxisM in measurements then begin
								PutFString(MajorLabel, fwidth);
								PutTabDelimeter;
							end;
						if MinorAxisM in measurements then begin
								PutFString(MinorLabel, fwidth);
								PutTabDelimeter;
							end;
						if (AngleM in measurements) or (nAngles > 0) then begin
								PutFString('Angle', fwidth);
								PutTabDelimeter;
							end;
						if IntDenM in measurements then begin
								PutFString('Int.Den.', fwidth + 2);
								PutTabDelimeter;
								PutFString('Back.', fwidth);
								PutTabDelimeter;
							end;
						if MinMaxM in measurements then begin
								PutFString('Min', fwidth);
								PutTabDelimeter;
								PutFString('Max', fwidth);
								PutTabDelimeter;
							end;
						if User1M in measurements then begin
								PutFString(User1Label, fwidth);
								PutTabDelimeter;
							end;
						if User2M in measurements then begin
								PutFString(User2Label, fwidth);
								PutTabDelimeter;
							end;
						PutChar(cr);
						PutChar(cr);
					end;
				for i := FirstCount to LastCount do begin
						column := 0;
						if Headings then
							PutSequenceNumber;
						if AreaM in measurements then begin
								PutReal(mArea^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if MeanM in measurements then begin
								PutReal(mean^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if StdDevM in measurements then begin
								PutReal(sd^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if (xyLocM in measurements) or (nPoints > 0) then begin
								PutReal(xcenter^[i], fwidth, precision);
								PutTab;
								PutReal(ycenter^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if ModeM in measurements then begin
								PutReal(mode^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if (LengthM in measurements) or (nLengths > 0) then begin
								PutReal(plength^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if MajorAxisM in measurements then begin
								PutReal(MajorAxis^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if MinorAxisM in measurements then begin
								PutReal(MinorAxis^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if (AngleM in measurements) or (nAngles > 0) then begin
								PutReal(orientation^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if IntDenM in measurements then begin
								PutReal(IntegratedDensity^[i], fwidth + 2, precision);
								PutTabDelimeter;
								PutReal(idBackground^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if MinMaxM in measurements then begin
								PutReal(mMin^[i], fwidth, precision);
								PutTabDelimeter;
								PutReal(mMax^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if User1M in measurements then begin
								PutReal(User1^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						if User2M in measurements then begin
								PutReal(User2^[i], fwidth, precision);
								PutTabDelimeter;
							end;
						PutChar(cr);
					end; {for}
			end; {with}
	end;


	procedure ShowWatch;
	begin
		SetCursor(watch);
	end;


	procedure ShowAnimatedWatch;
	begin
		SetCursor(AnimatedWatch[WatchIndex]);
		WatchIndex := WatchIndex + 1;
		if WatchIndex > 8 then
			WatchIndex := 1;
	end;


	procedure CaptureImage;
		var
			Timeout: LongInt;
			vdigErr: ComponentResult;
	begin
		case FrameGrabber of
			QuickCapture:  begin
					ControlReg^ := BitAnd($80, 255); {Start frame capture}
					while BitAnd(ControlReg^, $80) = $80 do
						;       {Wait for it to complete}
				end;
			ScionLG3, ScionAG5, ScionVG5f:  begin
					TimeOut := TickCount + 30;  {1/2sec. timeout}
					ControlReg^ := $80; {Start frame capture}
					while BitAnd(ControlReg^, $80) = $00 do begin    {Wait for it to complete}
							if TickCount > TimeOut then begin
									ControlReg^ := $00;
									leave
								end;
						end;
					ControlReg^ := $00;
				end;
			QTvdig:
				if vdig <> nil then
					vdigErr := VDGrabOneFrame(vdig);
		end; {case}
	end;


	procedure Paste;
		var
			srcPixMap: PixMapHandle;
			PCILivePaste: boolean;
	begin
		if info = NoInfo then begin
				beep;
				exit(Paste)
			end;
		with Info^ do begin
				if not RoiShowing then
					exit(Paste);
				if PasteTransferMode = SrcCopy then begin
						pmForeColor(BlackIndex);
						pmBackColor(WhiteIndex);
					end;
				srcPixMap := ClipBufInfo^.osPort^.PortPixMap;
				PCILivePaste := false;
				if LivePasteMode then
					if ((WhatsOnClip = CameraPic) or (WhatsOnClip = LivePic)) and (PictureType <> FrameGrabberType) then begin
							if PCIFrameGrabber then
								with fgPort^.PortPixMap^^ do begin
									BaseAddr := ptr(fgSlotBase);
									PCILivePaste := true;
								end;
							CaptureImage;
							srcPixMap := fgPixMap;
						end;
				CopyBits(BitMapHandle(srcPixMap)^^, BitMapHandle(osPort^.PortPixMap)^^, ClipBufInfo^.RoiRect, RoiRect, PasteTransferMode, roiRgn);
				if PCILivePaste then
					with fgPort^.PortPixMap^^ do
						BaseAddr := ptr(fgSuperSlotBase0);
				if PasteTransferMode = SrcCopy then begin
						pmForeColor(ForegroundIndex);
						pmBackColor(BackgroundIndex);
					end;
			end;
	end;


	procedure DoOperation (Operation: OpType);
		var
			tPort: GrafPtr;
			loc: point;
			width, height, SaveWidth: integer;
			tRect: rect;
			SaveGDevice: GDHandle;
	begin
		SaveGDevice := GetGDevice;
		GetPort(tPort);
		with Info^ do begin
				changes := true;
				SetGDevice(osGDevice);
				SetPort(GrafPtr(osPort));
				pmForeColor(ForegroundIndex);
				pmBackColor(BackgroundIndex);
				PenNormal;
				case Operation of
					InvertOp: 
						InvertRgn(roiRgn);
					PaintOp: 
						PaintRgn(roiRgn);
					FrameOp:  begin
							if (RoiType = LineRoi) or (RoiType = FreeLineRoi) or (RoiTYpe = SegLineRoi) then
								PenSize(1, 1)
							else
								PenSize(LineWidth, LineWidth);
							FrameRgn(roiRgn);
						end;
					EraseOp:begin 
							EraseRgn(roiRgn);
						end;
					PasteOp: 
						Paste;
					otherwise
				end;
				if not RoiShowing then begin
					UpdateScreen(RoiRect);
					end;
				if PixMapSize > UndoBufSize then
					OpPending := false;
			end;
		SetPort(tPort);
		SetGDevice(SaveGDevice);
	end;


	procedure SaveRoi;
	begin
		with info^ do
			if RoiType <> noRoi then begin
					NoInfo^.roiType := roiType;
					NoInfo^.RoiRect := RoiRect;
					CopyRgn(roiRgn, NoInfo^.roiRgn);
					NoInfo^.LX1 := LX1;
					NoInfo^.LY1 := LY1;
					NoInfo^.LX2 := LX2;
					NoInfo^.LY2 := LY2;
					NoInfo^.LAngle := LAngle;
				end;
	end;


	procedure KillRoi;
		var
			trect: rect;
	begin
		with info^ do begin
				if RoiShowing then begin
						if OpPending then begin
								OpPending := false;
								DoOperation(CurrentOp);
							end;
						SaveRoi;
						RoiShowing := false;
						trect := RoiRect;
						if RoiType = LineRoi then
							InsetRect(trect, -RoiHandleSize, -RoiHandleSize);
						UpdateScreen(trect);
					end;
				RoiType := NoRoi;
				RoiUpdateTime := 0;
			end;
	end;


	procedure ShowRoi;
	begin
		with info^ do
			if RoiType <> NoRoi then begin
					SetupUndo;
					RoiShowing := true;
				end;
	end;


	procedure SetupUndo;
		var
			line: integer;
	begin
		WhatToUndo := NothingToUndo;
		if info = NoInfo then begin
				CurrentUndoSize := 0;
				exit(SetupUndo)
			end;
		with info^ do begin
				if PixMapSize > UndoBufSize then begin
						CurrentUndoSize := 0;
						exit(SetupUndo)
					end;
				if OpPending then begin
						DoOperation(CurrentOp);
						OpPending := false;
					end;
				CurrentUndoSize := PixMapSize;
				BlockMove(PicBaseAddr, UndoBuf, PixMapSize);
				UndoFromClip := false;
				RedoSelection := false;
			end;
	end;


	procedure SetupUndoFromClip;
		var
			line: integer;
	begin
		WhatToUndo := NothingToUndo;
		if info = NoInfo then begin
				CurrentUndoSize := 0;
				exit(SetupUndoFromClip)
			end;
		with info^ do begin
				if PixMapSize > ClipBufSize then begin
						CurrentUndoSize := 0;
						exit(SetupUndoFromClip)
					end;
				if OpPending then begin
						DoOperation(CurrentOp);
						OpPending := false;
					end;
				CurrentUndoSize := PixMapSize;
				BlockMove(PicBaseAddr, ClipBuf, PixMapSize);
			end;
		WhatsOnClip := NothingOnClip;
		UndofromClip := true;
		RedoSelection := false;
	end;


	function NoSelection: boolean;
	begin
		if Info = NoInfo then begin
				beep;
				NoSelection := true;
				exit(NoSelection);
			end;
		if not Info^.RoiShowing then begin
				PutError('Please use a selection tool to make a selection or use the Select All command.');
				AbortMacro;
			end;
		NoSelection := not Info^.RoiShowing;
	end;


	function NotRectangular;{:boolean}
	begin
		with info^ do
			if RoiShowing and (RoiType <> RectRoi) then begin
					PutError('This operation requires a rectangular selection.');
					NotRectangular := true;
					AbortMacro;
				end
			else
				NotRectangular := false;
	end;


	procedure GetLoi (var x1, y1, x2, y2: extended);
	begin
		with info^, info^.RoiRect do begin
				x1 := left + LX1;
				y1 := top + LY1;
				x2 := left + LX2;
				y2 := top + LY2;
			end;
	end;


	function NotInBounds: boolean;
		var
			x1, y1, x2, y2: extended;
	begin
		NotInBounds := false;
		with info^, info^.RoiRect do
			if RoiShowing then begin
					if RoiType = LineRoi then begin
							GetLoi(x1, y1, x2, y2);
							if (x1 >= 0.0) and (y1 >= 0.0) and (x2 <= right) and (y2 <= bottom) then
								exit(NotInBounds);
						end;
					if (left < 0) or (top < 0) or (right > PicRect.right) or (bottom > PicRect.bottom) then begin
							PutError('This operation requires the selection to be entirely within the image.');
							NotInBounds := true;
							AbortMacro;
						end;
				end;
	end;


	function NoUndo: boolean;
		var
			ImageTooLarge: boolean;
	begin
		with info^ do
			ImageTooLarge := (PixMapSize > ClipBufSize) or (PixMapSize > UndoBufSize);
		if ImageTooLarge then
			PutError('This operation requires that the Undo and Clipboard buffers be at least as large as the image.');
		NoUndo := ImageTooLarge;
	end;



	procedure PutMemoryAlert;
	begin
		if not OpeningFinderFiles then
			PutError('There is not enough free memory to open this image. Try closing some windows or allocating more memory to NIH Image.');
		AbortMacro;
	end;


	procedure CompactMemory;
		var
			size: LongInt;
			TempInfo: InfoPtr;
			i: integer;
	begin
		for i := 1 to nPics do begin
				TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
				hunlock(TempInfo^.PicBaseHandle)
			end;
		size := MaxSize;
		size := MaxMem(size);
		for i := 1 to nPics do begin
				TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
				with TempInfo^ do begin
						hlock(PicBaseHandle);
						{$ifc PowerPC}
						PicBaseAddr := PicBaseHandle^;
						{$elsec}
						PicBaseAddr := StripAddress(PicBaseHandle^);
						{$endc}
						osPort^.PortPixMap^^.BaseAddr := PicBaseAddr;
					end;
			end;
	end;



	function GetBigHandle (NeededSize: LongInt): handle;
{Allocates a handle and guarantees MinFree contiguous free bytes after allocation . }
{Does NOT arrange for the new handle to be unlocked during CompactMemory. }
{GetBigHandle returns nil if CompactMemory fails to obtain enough contiguous free space . }
		var
			h: handle;
			FreeMem: LongInt;
	begin
		h := NewHandle(NeededSize);
		FreeMem := MaxBlock;
		if (h = nil) or (FreeMem < MinFree) then begin
				if h <> nil then
					DisposeHandle(h);
				if FreeMem > 0 then {Why does FreeMem get set to 0 sometimes and MaxMem}
					CompactMemory       {crash, but only when using the Modern Memory Manager?}
				else
					beep;
				h := NewHandle(NeededSize);
				FreeMem := MaxBlock;
			end;
		if (h = nil) or (FreeMem < MinFree) then begin
				if h <> nil then
					DisposeHandle(h);
				h := nil;
			end;
		GetBigHandle := h;
	end;


	function GetImageMemory (SaveInfo: infoPtr): ptr;
{Allocates memory for the PixMap of new image windows. SaveInfo points to the InfoRec of the previous window.}
{A handle is used, rather than a pointer, since NewPtr(particularly on the ci and fx) is rediculously slow.}
		var
			h: handle;
			NeededSize: LongInt;
	begin
		with info^ do begin
				if odd(PixelsPerLine) then
					BytesPerRow := PixelsPerLine + 1
				else
					BytesPerRow := PixelsPerLine;
				PixMapSize := nlines * BytesPerRow;
				ImageSize := nlines * PixelsPerLine;
				NeededSize := PixMapSize;
			end;
		h := GetBigHandle(NeededSize);
		if h = nil then begin
				DisposePtr(pointer(Info));
				PutMemoryAlert;
				Info := SaveInfo;
				GetImageMemory := nil;
				exit(GetImageMemory);
			end;
		with info^ do begin
				PicBaseHandle := h;
				hlock(PicBaseHandle);
				{$ifc PowerPC}
				GetImageMemory := PicBaseHandle^;
				{$elsec}
				GetImageMemory := StripAddress(PicBaseHandle^);
				{$endc}
			end;
	end;


	procedure UpdateAnalysisMenu;
		var
			ShowItems: boolean;
			i: integer;
	begin
		ShowItems := Info <> NoInfo;
		SetMenuItem(AnalyzemenuH, MeasureItem, ShowItems);
		SetMenuItem(AnalyzemenuH, AnalyzeItem, ShowItems);
		SetMenuItem(AnalyzemenuH, HistogramItem, ShowItems);
		SetMenuItem(AnalyzemenuH, PlotItem, ShowItems);
		SetMenuItem(AnalyzemenuH, PlotSurfaceItem, ShowItems);
		SetMenuItem(AnalyzemenuH, SetScaleItem, ShowItems);
		SetMenuItem(AnalyzemenuH, CalibrateItem, ShowItems);
		SetMenuItem(AnalyzemenuH, RedoItem, mCount > 0);
		SetMenuItem(AnalyzemenuH, DeleteItem, mCount > 0);
		SetMenuItem(AnalyzemenuH, RestoreItem, ShowItems and (NoInfo^.RoiType <> NoRoi));
		SetMenuItem(AnalyzemenuH, MarkItem, info^.RoiShowing);
	end;


	procedure ExtendWindowsMenu (fname: str255; size: LongInt; wptr: WindowPtr);
		var
			str, SizeStr: str255;
	begin
		if nPics < MaxPics then begin
				nPics := nPics + 1;
				PicWindow[nPics] := wptr;
				NumToString((size + 511) div 1024, SizeStr);
				str := concat(fname, '  ', SizeStr, 'K');
				AppendMenu(WindowsMenuH, ' ');
				SetMenuItemText(WindowsMenuH, nPics + WindowsMenuItems + nTextWindows, str);
				InsertMenu(WindowsMenuH, 0);
			end;
	end;


	procedure InvertGrayLevels;
	begin
		with info^ do begin
				fit := StraightLine;
				nCoefficients := 2;
				Coefficient[1] := 255.0;
				Coefficient[2] := -1.0;
				ZeroClip := false;
				UnitOfMeasure := '';
				nKnownValues := 0;
				NoInfo^.fit := StraightLine;
				NoInfo^.nCoefficients := 2;
				NoInfo^.Coefficient := Coefficient;
				NoInfo^.ZeroClip := false;
				NoInfo^.UnitOfMeasure := '';
				GenerateValues;
				UpdateTitleBar;
			end;
	end;


	function GetAngle (dx, dy: extended):extended;
		var
			angle:extended;
			quadrant: (q1, q2orq3, q4);
	begin
		if dx <> 0.0 then
			angle := arctan(dy / dx)
		else begin
				if dy >= 0.0 then
					angle := pi / 2.0
				else
					angle := -pi / 2.0
			end;
		angle := (180.0 / pi) * angle;
		if (dx >= 0.0) and (dy >= 0.0) then
			quadrant := q1
		else if dx < 0.0 then
			quadrant := q2orq3
		else
			quadrant := q4;
		case quadrant of
			q1: 
				;
			q2orq3: 
				angle := angle + 180.0;
			q4: 
				angle := angle + 360.0;
		end;
		GetAngle:=angle; {ppc-bug}
	end;


	procedure MakeRegion;
		var
			deltax, deltay, x1, y1, x2, y2, xt, yt: integer;
			dx, dy, pAngle: extended;
			add: boolean;
			tPort: GrafPtr;
	begin
		with info^ do begin
				GetPort(tPort);
				SetPort(wptr);
				OpenRgn;
				case RoiType of
					LineRoi:  begin
							LAngle:=GetAngle(LX2 - LX1, LY1 - LY2);
							x1 := round(LX1);
							y1 := round(LY1);
							x2 := round(LX2);
							y2 := round(LY2);
							if (x1 = x2) and (y1 = y2) then begin
									MoveTo(x1, y1);
									LineTo(x1 + 1, y1);
									LineTo(x1 + 1, y1 + 1);
									LineTo(x1, y1 + 1);
									LineTo(x1, y1);
								end
							else begin
									add := (LAngle > 90.0) and (LAngle <= 270.0);
									pAngle := (LAngle / 180.0) * pi;
									if add then
										pAngle := pAngle + pi / 2.0
									else
										pAngle := pAngle - pi / 2.0;
									dx := cos(pAngle) * LineWidth;
									dy := -sin(pAngle) * LineWidth;
									MoveTo(x1, y1);
									LineTo(round(x1 + dx), round(y1 + dy));
									LineTo(round(x2 + dx), round(y2 + dy));
									LineTo(x2, y2);
									LineTo(x1, y1);
								end;
						end;
					OvalRoi: 
						FrameOval(RoiRect);
					RectRoi: 
						FrameRect(RoiRect);
					otherwise
				end;
				CloseRgn(roiRgn);
				if RoiType = LineRoi then begin
						RoiRect := roiRgn^^.rgnBBox;
						with RoiRect do begin
								LX1 := LX1 - left;
								LY1 := LY1 - top;
								LX2 := LX2 - left;
								LY2 := LY2 - top;
							end;
					end;
			end;
		SetPort(tPort);
	end;


	procedure SelectAll (visible: boolean);
		var
			loc: point;
			tPort: GrafPtr;
	begin
		if info <> NoInfo then
			with Info^ do begin
					KillRoi;
					RoiType := RectRoi;
					RoiRect := PicRect;
					MakeRegion;
					if visible then begin
							SetupUndo;
							RoiShowing := true;
							if (magnification > 1.0) and not ScaleToFitWindow then
								Unzoom;
							if not macro then begin
									PreviousTool := CurrentTool;
									CurrentTool := SelectionTool;
									isSelectionTool := true;
									GetPort(tPort);
									SetPort(ToolWindow);
									EraseRect(ToolRect[PreviousTool]);
									EraseRect(ToolRect[CurrentTool]);
									InvalRect(ToolRect[PreviousTool]);
									InvalRect(ToolRect[CurrentTool]);
									SetPort(tPort);
								end;
						end;
					IsInsertionPoint := false;
					measuring := false;
				end; {with}
	end;


	procedure KillOperation;
	begin
		if OpPending then
			with info^ do
				if info <> NoInfo then begin
						DoOperation(CurrentOp);
						RoiShowing := false;
						UpdateScreen(RoiRect);
						OpPending := false;
					end;
	end;


	procedure CloneInfo (var OldInfo, NewInfo: PicInfo);
	begin
		NewInfo := OldInfo;
		with NewInfo do begin
				PicBaseAddr := nil;
				PicBaseHandle := nil;
				osPort := nil;
				roiRgn := nil;
				RoiType := NoRoi;
				RoiShowing := false;
				Magnification := 1.0;
				vref := 0;
				wPtr := nil;
				ScaleToFitWindow := false;
				WindowState := NormalWindow;
				StackInfo := nil;
				fileVersion := 0;
				PictureType := NewPicture;
				DataType := EightBits;
				changes := false;
				DataH := nil;
				LittleEndian := false;
				InvertedImage := false;
				if OldInfo.DataH <> nil then {real image}
					fit := uncalibrated;
				if (not SpatiallyCalibrated and (fit=uncalibrated)) or (nPics=0) then begin
					if NoInfo^.SpatiallyCalibrated then begin
						SpatiallyCalibrated:=true;
						xUnit := NoInfo^.xUnit;
						xScale := NoInfo^.xScale;
						PixelAspectRatio := NoInfo^.PixelAspectRatio;
						yScale := xScale / PixelAspectRatio;
					end;
					if NoInfo^.fit<>uncalibrated then begin
						fit := NoInfo^.fit;
						nCoefficients := NoInfo^.nCoefficients;
						Coefficient := NoInfo^.Coefficient;
						ZeroClip := NoInfo^.ZeroClip;
						UnitOfMeasure := NoInfo^.UnitOfMeasure;
					end;
				end;
			end;
	end;


	function NewPicWindow (name: str255; width, height: integer): boolean;
		var
			iptr, p: ptr;
			lptr: ^LongInt;
			SaveInfo: InfoPtr;
			NeededSize: LongInt;
			trect: rect;
	begin
		NewPicWindow := false;
		PicLeft := PicLeftBase;
		PicTop := PicTopBase;
		if (info <> noInfo) then begin
				with info^ do begin
						GetWindowRect(wptr, trect);
						if trect.left = PicLeftBase then
							if pos('Camera', name) = 0 then begin
									PicLeft := trect.left + hPicOffset;
									PicTop := trect.top + vPicOffset;
								end;
					end;
			end;
		if nPics = MaxPics then
			exit(NewPicWindow);
		KillOperation;
		DisableDensitySlice;
		SaveInfo := Info;
		iptr := NewPtr(SizeOf(PicInfo));
		if iptr = nil then begin
				PutMemoryAlert;
				AbortMacro;
				exit(NewPicWindow);
			end;
		Info := pointer(iptr);
		CloneInfo(SaveInfo^, Info^);
		with Info^ do begin
				nlines := height;
				PixelsPerLine := width;
				p := GetImageMemory(SaveInfo);
				if p = nil then
					exit(NewPicWindow);
				PicBaseAddr := p;
				MakeNewWindow(name);
				SelectAll(false);
				if not OptionKeyDown then DoOperation(EraseOp);
				KillRoi;
				Changes := false;
				BinaryPic := false;
			end;
		UpdateTitleBar;
		NewPicWindow := true;
	end;


	procedure EraseScreen;
	begin
		SetPort(GrafPtr(CScreenPort));
		with CScreenPort^ do begin
				HideCursor;
				pmBackColor(BackgroundIndex);
				EraseRect(portPixMap^^.Bounds);
				pmBackColor(WhiteIndex);
			end;
	end;


	procedure RestoreScreen;
		var
			GrayRgn: RgnHandle;
			rptr: rhptr;
			wp: ^WindowPtr;
	begin
		rptr := rhptr(GrayRgnGlobal);
		GrayRgn := rptr^;
		wp := pointer(GhostWindow);
		wp^ := WindowPtr(nil);
		PaintBehind(WindowRef(FrontWindow), GrayRgn);
		wp^ := PasteControl;
		DrawMenuBar;
		InitCursor;
	end;


	procedure UpdateTitleBar;
    {Updates the window title bar to show the current magnification or the current frame within a stack.}
		var
			str, str2, str3: str255;
			SaveGDevice: GDHandle;
	begin
		if info = NoInfo then
			exit(UpdateTitleBar);
		with info^ do begin
				str := title;
				if info^.DataH <> nil then
					str := concat('<<',str, '>>');
				if SpatiallyCalibrated then
					str := concat(str, chr($13)); {Black Diamond}
				if fit <> uncalibrated then
					str := concat(str, '×');
				if StackInfo <> nil then
					with StackInfo^ do
						if (nSlices = 3) and (StackType = rgbStack) then begin
								case CurrentSlice of
									1: str2 := 'Red';
									2: str2 := 'Green';
									3: str2 := 'Blue';
								end;
								str := concat(str, ' (', str2, ')');
						end else begin
								NumToString(CurrentSlice, str2);
								NumToString(nSlices, str3);
								str := concat(str, ' (', str2, '/', str3, ')');
						end
				else if (magnification <> 1.0) or ScaleToFitWindow then begin
						if ScaleToFitWindow then begin
								RealToString(magnification, 1, 2, str2);
								str := concat(str, ' (', str2, ')');
							end
						else begin
								RealToString(magnification, 1, 0, str2);
								str := concat(str, ' (', str2, ':1)');
							end;
					end;
				if Digitizing then begin
						if ExternalTrigger then
							str := concat(str, ' (Waiting for Trigger)')
						else
							str := concat(str, ' (Live)');
					end;
				if wptr <> nil then begin
					SaveGDevice := GetGDevice;
					SetGDevice(GetMainDevice);
					SetWTitle(wptr, str);
					SetGDevice(SaveGDevice);
					end;
			end; {with}
	end;


	procedure ScaleToFit;
		var
			trect: rect;
	begin
		if digitizing then
			exit(ScaleToFit);
		if info <> NoInfo then
			with info^ do begin
					ScaleToFitWindow := not ScaleToFitWindow;
					KillRoi;
					if ScaleToFitWindow then begin
							savewrect := wrect;
							SaveSrcRect := SrcRect;
							SaveMagnification := magnification;
							GetWindowRect(wptr, trect);
							savehloc := trect.left;
							savevloc := trect.top;
							wrect := wptr^.PortRect;
							SrcRect := PicRect;
							ScaleImageWindow(wrect);
							SizeWindow(wptr, wrect.right, wrect.bottom, true);
						end
					else begin
							if WindowState = TiledBigScaled then begin
									wrect := initwrect;
									SrcRect := wrect;
									magnification := 1.0;
									WindowState := NormalWindow;
								end
							else begin
									wrect := savewrect;
									SrcRect := SaveSrcRect;
									magnification := SaveMagnification;
								end;
							HideWindow(wptr);
							SizeWindow(wptr, wrect.right, wrect.bottom, true);
							MoveWindow(wptr, savehloc, savevloc, true);
							ShowWindow(wptr);
							UpdateTitleBar;
						end;
					SetPort(wptr);
					InvalRect(wrect);
					WindowState := NormalWindow;
				end;
	end;


	procedure DrawMyGrowIcon (w: WindowPtr);
		var
			tPort: GrafPtr;
			tRect: rect;
	begin
		GetPort(tPort);
		SetPort(w);
		PenNormal;
		with w^.PortRect do begin
				SetRect(tRect, right - 12, bottom - 12, right - 5, bottom - 5);
				FrameRect(tRect);
				MoveTo(right - 6, bottom - 10);
				LineTo(right - 2, bottom - 10);
				LineTo(right - 2, bottom - 2);
				LineTo(right - 10, bottom - 2);
				LineTo(right - 10, bottom - 6);
			end;
		SetPort(tPort);
	end;


	procedure Unzoom;
	begin
		if Info <> NoInfo then
			with Info^ do begin
					ScaleToFitWindow:=false;
					wrect := initwrect;
					SrcRect := wrect;
					SizeWindow(wptr, wrect.right, wrect.bottom, true);
					LoadLUT(info^.cTable);
					UpdatePicWindow;
					magnification := 1.0;
					DrawMyGrowIcon(wptr);
					UpdateTitleBar;
					WindowState:=NormalWindow;
					if WhatToUndo = UndoZoom then
						WhatToUndo := NothingToUndo;
					ShowRoi;
				end;
	end;


	procedure DrawBString(str:string);
	var
		s:style;
	begin
		TextFace([bold]);
		DrawString(str);
		s:=[];  {ppc-bug}
		TextFace(s);
	end;


	function long2str (num: LongInt): str255;
		var
			str: str255;
	begin
		NumToString(num, str);
		long2str := str;
	end;


	procedure PutWarning;
	begin
		PutError(concat('This ', long2str((info^.PixmapSize + 511) div 1024), 'K image is larger than the ', long2str(UndoBufSize div 1024), 'K Undo buffer. Many operations may fail or may not be Undoable.'));
	end;


	procedure SetupRoiRect;
{Copies the current image to Undo buffer so it can be used for drawing}
{the "marching ants". The copy of the previous image in the Clipboard buffer}
{ buffer will be used for Undo.}
		var
			SaveWhatToUndo: WhatToUndoType;
	begin
		SaveWhatToUndo := WhatToUndo;
		SetupUndo;
		UndoFromClip := true;
		info^.RoiShowing := true;
		WhatToUndo := SaveWhatToUndo;
	end;


	procedure SetForegroundColor (color: integer);
		var
			tPort: GrafPtr;
			SaveGDevice: GDHandle;
	begin
		if (color >= 0) and (color <= 255) then
			with info^ do begin
					ForegroundIndex := color;
					GetPort(tPort);
					SetPort(ToolWindow);
					InvalRect(ToolRect[brush]);
					SaveGDevice := GetGDevice;
					SetGDevice(osGDevice);
					if osPort <> nil then begin
							SetPort(GrafPtr(osPort));
							pmForeColor(ForegroundIndex);
						end;
					SetPort(tPort);
					SetGDevice(SaveGDevice);
					if isInsertionPoint then
						DisplayText(true);
				end;
	end;


	procedure SetBackgroundColor (color: integer);
		var
			tPort: GrafPtr;
			SaveGDevice: GDHandle;
	begin
		if (color >= 0) and (color <= 255) then
			with info^ do begin
					BackgroundIndex := color;
					GetPort(tPort);
					SetPort(ToolWindow);
					InvalRect(ToolRect[eraser]);
					SaveGDevice := GetGDevice;
					SetGDevice(osGDevice);
					if osPort <> nil then begin
							SetPort(GrafPtr(osPort));
							pmBackColor(BackgroundIndex);
						end;
					SetPort(tPort);
					SetGDevice(SaveGDevice);
					if isInsertionPoint then
						DisplayText(true);
				end;
	end;


	procedure GetForegroundColor (event: EventRecord);
		var
			loc: point;
			color: integer;
	begin
		loc := event.where;
		ScreenToOffScreen(loc);
		Color := MyGetPixel(loc.h, loc.v);
		SetForegroundColor(color);
	end;


	procedure GetBackgroundColor; {(event: EventRecord)}
		var
			loc: point;
			color: integer;
	begin
		loc := event.where;
		ScreenToOffScreen(loc);
		Color := MyGetPixel(loc.h, loc.v);
		SetBackgroundColor(color);
	end;


procedure GenerateValues;
		var
			a, b, c, d, e, f, x, y: extended;
			i: integer;
	begin
		with info^ do begin
				if fit = uncalibrated then begin
						for i := 0 to 255 do
							cvalue[i] := i;
						minCValue := 0.0;
						maxCValue := 255.0;
						exit(GenerateValues);
					end;
				a := Coefficient[1];
				b := Coefficient[2];
				c := Coefficient[3];
				d := Coefficient[4];
				e := Coefficient[5];
				f := Coefficient[6];
				minCValue := 10e+12;
				maxCValue := -minCValue;
				for i := 0 to 255 do begin
						x := i;
						case fit of
							StraightLine: 
								y := a + b * x;
							Poly2: 
								y := a + b * x + c * x * x;
							Poly3: 
								y := a + b * x + c * x * x + d * x * x * x;
							Poly4: 
								y := a + b * x + c * x * x + d * x * x * x + e * x * x * x * x;
							Poly5: 
								y := a + b * x + c * x * x + d * x * x * x + e * x * x * x * x + f * x * x * x * x * x;
							ExpoFit: 
								y := a * exp(b * x);
							PowerFit: 
								if x = 0.0 then
									y := 0.0
								else
									y := a * exp(b * ln(x)); {y=ax^b}
							LogFit:  begin
									if x = 0.0 then
										x := 0.5;
									y := a * ln(b * x)
								end;
							RodbardFit:  begin
									if x <= a then
										y := 0
									else begin
											y := (a - x) / (x - d);
											y := exp(ln(y) * (1 / b));  {y:=y**(1/b)}
											y := y * c;
										end;
								end;
							UncalibratedOD:  begin
									if x = 255.0 then
										x := 254.5;
									y := 0.434294481 * ln(255.0 / (255.0 - x))  {log10}
								end;
							otherwise
								y := x;
						end; {case}
						cvalue[i] := y;
						if y > maxCValue then
							maxCValue := y;
						if y < minCValue then
							minCValue := y;
					end; {for}
				if minCValue >= 0.0 then
					ZeroClip := false;
				if ZeroClip then begin
						for i := 0 to 255 do
							if cvalue[i] < 0.0 then
								cvalue[i] := 0.0;
						minCValue := 0.0;
					end;
			end;
	end;


	procedure ScaleImageWindow (var trect: rect);
		var
			WindowLeft, WindowTop: integer;
			PicAspectRatio, TempMagnification: extended;
	begin
		with info^ do begin
				SrcRect := PicRect;
				with CGrafPtr(wptr)^.PortPixMap^^.bounds do begin
						WindowLeft := -left;
						WindowTop := -top;
					end;
	with PicRect do
					PicAspectRatio := right / bottom;
				with trect do begin
						if (WindowLeft + right) > (ScreenWidth - 5) then
							right := ScreenWidth - 5 - WindowLeft;
						bottom := round(right / PicAspectRatio);
						if (WindowTop + bottom) > (ScreenHeight - 5) then
							bottom := ScreenHeight - 5 - WindowTop;
						right := round(bottom * PicAspectRatio);
						magnification := right / PicRect.right;
					end;
				UpdateTitleBar;
			end; {with}
	end;


	function TooWide: boolean;
		var
			SelectionTooWide: boolean;
			MaxWidth: str255;
	begin
		with info^.RoiRect do
			SelectionTooWide := (right - left) > MaxLine;
		if SelectionTooWide then begin
				NumToString(MaxLine, MaxWidth);
				PutError(concat('This operation does not support selections wider than ', MaxWidth, ' pixels.'));
				AbortMacro;
			end;
		TooWide := SelectionTooWide;
	end;


	procedure DrawTextString (str: str255; loc: point; just: integer);
		var
			SaveJust: integer;
	begin
		TextStr := str;
		IsInsertionPoint := true;
		TextStart := loc;
		SaveJust := TextJust;
		TextJust := just;
		DisplayText(false);
		TextJust := SaveJust;
		IsInsertionPoint := false;
	end;


	procedure IncrementCounter;
	begin
		if mCount < MaxMeasurements then begin
				mCount := mCount + 1;
				UnsavedResults := true;
			end
		else
			beep;
	end;


	procedure ClearResults (i: integer);
	begin
		mean^[i] := 0.0;
		sd^[i] := 0.0;
		PixelCount^[i] := 0;
		mArea^[i] := 0.0;
		mode^[i] := 0.0;
		IntegratedDensity^[i] := 0.0;
		idBackground^[i] := 0.0;
		xcenter^[i] := 0.0;
		ycenter^[i] := 0.0;
		MajorAxis^[i] := 0.0;
		MinorAxis^[i] := 0.0;
		orientation^[i] := 0.0;
		mMin^[i] := 0.0;
		mMax^[i] := 0.0;
		plength^[i] := 0.0;
	end;

	procedure UpdateFitEllipse;
	begin
		FitEllipse :=(xyLocM in measurements) or (MajorAxisM in measurements) or (MinorAxisM in measurements) or (AngleM in measurements);
	end;



	function StringToReal (str: str255): extended;
		var
			i, ndigits, StringLength: integer;
			c: char;
			n, m: extended;
			negative, LeftOfPoint, NegExp: boolean;
			exponent: LongInt;
	begin
		negative := false;
		n := 0.0;
		LeftOfPoint := true;
		m := 0.1;
		ndigits := 0;
		StringLength := length(str);
		i := 0;
		repeat
			i := i + 1;
		until (str[i] in ['0'..'9', '-', '.']) or (i >= StringLength);
		c := str[i];
		repeat
			if c = '-' then
				negative := true
			else if c = '.' then
				LeftOfPoint := false
			else if (c >= '0') and (c <= '9') then begin
					ndigits := ndigits + 1;
					if LeftOfPoint then
						n := n * 10.0 + ord(c) - ord('0')
					else begin
							n := n + (ord(c) - ord('0')) * m;
							m := m * 0.1;
						end;
				end;
			i := i + 1;
			if i <= StringLength then
				c := str[i];
		until not (c in ['0'..'9', '-', '.']) or (i > StringLength);
		if (c = 'e') or (c = 'E') then begin
				NegExp := false;
				exponent := 0;
				i := i + 1;
				if i <= StringLength then
					c := str[i];
				if (c = '+') or (c = '-') then begin
						if c = '-' then
							NegExp := true;
						i := i + 1;
						if i <= StringLength then
							c := str[i];
					end;
				repeat
					if (c >= '0') and (c <= '9') then
						exponent := exponent * 10 + ord(c) - ord('0');
					i := i + 1;
					if i <= StringLength then
						c := str[i];
				until not (c in ['0'..'9']) or (i > StringLength);
				if negExp then
					exponent := -exponent;
				if exponent <> 0 then
					n := n * exp(exponent * ln(10));
			end; {if c='e'}
		if ndigits = 0 then
			n := BadReal
		else if negative then
			n := -n;
		StringToReal := n;
	end;


	procedure RemovePath(var str: str255);
	var
		loc: integer;
	begin
		repeat
			loc := pos(':', str);
			if loc > 0 then
				delete(str, 1, loc);
		until loc = 0;
	end;


	procedure MakeNewWindow (name: str255);
		var
			wwidth, wheight, wleft, wtop, i: integer;
			tPort: GrafPtr;
			rgb: RGBColor;
			err: OSErr;
			str: str255;
			SaveGDevice: GDHandle;
	begin
		with Info^ do begin
				RemovePath(name);
				wleft := PicLeft;
				wtop := PicTop;
				PicLeft := PicLeft + hPicOffset;
				PicTop := PicTop + vPicOffset;
				if ((PicLeft + round(0.75 * PixelsPerLine)) > ScreenWidth) or ((PicTop + round(0.75 * nlines)) > ScreenHeight) then begin
						PicLeft := PicLeftBase;
						PicTop := PicTopBase;
					end;
				wwidth := PixelsPerLine;
				if (wleft + wwidth) > ScreenWidth then
					wwidth := ScreenWidth - wleft - 4;
				wheight := nlines;
				if (wtop + wheight) > ScreenHeight then
					wheight := ScreenHeight - wtop - 4;
				if OpeningPlugInWindow then
					SetRect(wrect, -10000, wtop, -10000 + wwidth, wtop + wheight)
				else
					SetRect(wrect, wleft, wtop, wleft + wwidth, wtop + wheight);
				str := name;
				if SpatiallyCalibrated then
					str := concat(str, chr($13)); {Black Diamond}
				if fit <> uncalibrated then
					str := concat(str, '×');
				wptr := NewCWindow(nil, wrect, str, true, DocumentProc + ZoomDocProc, nil, true, 0);
				GetPort(tPort);
				SetPort(wptr);
				SetPalette(wptr, ExplicitPalette, false);
				pmForeColor(BlackIndex);
				pmBackColor(WhiteIndex);
				SetRect(wrect, 0, 0, wwidth, wheight);
				SetRect(PicRect, 0, 0, PixelsPerLine, nlines);
				SelectWindow(wptr);
				WindowPeek(wptr)^.WindowKind := PicKind;
				WindowPeek(wptr)^.RefCon := ord4(Info);
				TruncateString(name, maxTitle);
				title := name;
				ExtendWindowsMenu(name, PixMapSize, wptr);
				PicNum := nPics;
				PidNum := nextPid;
				nextPid := nextPid - 1;
				osPort := CGrafPtr(NewPtr(SizeOf(CGrafPort)));
				SaveGDevice := GetGDevice;
				SetGDevice(osGDevice);
				OpenCPort(osPort);
				with osPort^ do begin
						with PortPixMap^^ do begin
								BaseAddr := PicBaseAddr;
								bounds := PicRect;
								pixelType := 0;
								if PixelSize > 8 then
									PixelSize := 8;
								cmpCount := 1;
							end;
						PortRect := PicRect;
						RectRgn(visRgn, PicRect);
						PortPixMap^^.RowBytes := BitOr(BytesPerRow, $8000);
					end;
				SetPalette(WindowPtr(osPort), ExplicitPalette, false);
				pmForeColor(ForegroundIndex);
				pmBackColor(BackgroundIndex);
				SetGDevice(SaveGDevice);
				SetPort(tPort);
				SrcRect := wrect;
				magnification := 1.0;
				RoiShowing := false;
				roiType := NoRoi;
				initwrect := wrect;
				savewrect := wrect;
				SaveSrcRect := SrcRect;
				SaveMagnification := magnification;
				savehloc := wleft;
				savevloc := wtop;
				roiRgn := NewRgn;
				NewPic := true;
				ScaleToFitWindow := false;
				OpPending := false;
				Changes := false;
				WindowState := NormalWindow;
				if (fit = uncalibrated) and InvertPixelValues then
					InvertGrayLevels;
				Revertable := false;
			end;
		WhatToUndo := NothingToUndo;
	end;


	procedure MakeLowerCase (var str: str255);
		var
			i: integer;
			c: char;
	begin
		for i := 1 to length(str) do begin
				c := str[i];
				if (c >= 'A') and (c <= 'Z') then
					str[i] := chr(ord(c) + 32);
			end;
	end;


	function PutMessageWithCancel (str: str255): integer;
	begin
		InitCursor;
		ParamText(str, '', '', '');
		PutMessageWithCancel := Alert(800, nil);
	end;


	function CurrentWindow: integer;
	begin
		CurrentWPtr := FrontWindow;
		if CurrentWPtr <> nil then begin
				CurrentKind := WindowPeek(CurrentWPtr)^.WindowKind;
				if CurrentKind = TextKind then
					TextInfo := TextInfoPtr(WindowPeek(CurrentWPtr)^.RefCon);
				CurrentWindow := CurrentKind;
			end
		else begin
				CurrentWindow := 0;
				CurrentKind := 0;
			end;
	end;


	procedure FindMonitors (NewScreenDepth: integer);
  {Generate a list of 8-bit monitors so we can update their LUTs.}
  {This wouldn't be necessary if we were using the Palette Manager.}
		var
			nextDevice: GDHandle;
	begin
		nMonitors := 0;
		nextDevice := GetDeviceList;
		while nextDevice <> nil do begin
				if TestDeviceAttribute(nextDevice, screenDevice) and TestDeviceAttribute(nextDevice, screenActive) then
					if nextDevice^^.gdPmap^^.PixelSize = 8 then begin
							nMonitors := nMonitors + 1;
							Monitors[nMonitors] := nextDevice;
						end;
				nextDevice := GetNextDevice(nextDevice);
			end; {while}
		if NewScreenDepth < 4 then
			gCopyMode := DitherCopy
		else
			gCopyMode := SrcCopy;
		SaveScreenDepth := NewScreenDepth;
	end;


	function ScreenDepth: integer;
		var
			depth: integer;
	begin
		depth := ScreenPixMap^^.PixelSize;
		if (depth = 8) and LUTFriendlyMode then
			depth := 6;
		if depth <> SaveScreenDepth then
			FindMonitors(depth);
		ScreenDepth := depth;
	end;


	procedure SetFColor (index: integer);
  {Sets the screen foreground color. Use pmForeColor to set the offscreen color.}
	begin
		if ScreenDepth = 8 then
			pmForeColor(index)
		else
			RGBForeColor(osGDevice^^.gdPMap^^.pmTable^^.ctTable[index].rgb);
	end;

	procedure SetBColor (index: integer);
  {Sets the screen background color.}
	begin
		if ScreenDepth = 8 then
			pmBackColor(index)
		else
			RGBBackColor(osGDevice^^.gdPMap^^.pmTable^^.ctTable[index].rgb);
	end;
	
	
	function DoubleToReal(d:FakeDouble):extended;
	{Converts an IEEE double to an IEEE float. Will not be needed
	when "8 Byte Doubles" work in the Metrowerks 68k compiler.}
	var
	  s, f, r:extended;
	  e:LongInt;
	  dd:double;
	begin
		{$ifc PowerPC}
		dd:=double(d);
		r:=dd;
		{$elsec PowerPC}
		if band(d[1],$80000000)=0 then
			s:=1
		else
			s:=-1;
		e:=band(d[1],$7ff00000);
		e:=bsr(e,20);
		f:=band(d[1],$fffff);
		f:=f / 1048576.0;
		f:=f + bsr(d[2],24)/268435456.0;
		{ShowMessage(StringOf('s=',s , ' e=', e, ' f=', f));}
		if (e > 0) and (e < 2047) then 
			r:=s * exp((e-1023)*ln(2.0)) * (1.0 + f)
		else if (e = 0) and (f <> 0) then 
			r:=s * f * exp(-1022.0*ln(2.0)) * f
		else if (e = 0) and (e = 0) then
			r:=0.0
		else if (e = 255) and (f = 0) then
			r:=0.0 {inf}
		else {if e=255 and f<>0}
			r:=0.0; {nan}
		{$endc PowerPC}
		DoubleToReal:=r;
	end;


	procedure RealToDouble(rr: extended; var d:FakeDouble);
	{Converts an IEEE float to an IEEE double. Will not be needed
	when "8 Byte Doubles" work in the Metrowerks 68k compiler.}
	var
	  i, s, e, f:LongInt;
	  r:real;
	  dd:double;
	begin
		{$ifc PowerPC}
		dd:=rr;
		d:=FakeDouble(dd);
		{$elsec PowerPC}
		r:=rr;
		i:=LongInt(r);
	  s:=band(i,$80000000);
	  e:=band(i,$7f800000);
		e:=bsr(e, 23);
		if e>255 then
			e:=255;
		e:=e-127+1023;
		e:=bsl(e, 20);
		f:=band(i, $7fffff);
		f:=bsr(f, 3);
		d[1]:=bor(s,bor(e,f));
		d[2]:=0;
		{if r<>0.0 then begin
			ShowMessage(StringOf(' e=', e,' f=', f)); wait(60);
		end;}
		{$endc PowerPC}
	end;
	
	
{$S Utilities2}
{Routines from here to the end of the file go in the Utilities2 segment}

	function MakeStackFromWindow: boolean;
	begin
		with info^ do begin
				StackInfo := StackInfoPtr(NewPtr(SizeOf(StackInfoRec)));
				if StackInfo = nil then begin
						MakeStackFromWindow := false;
						exit(MakeStackFromWindow);
					end;
				with StackInfo^ do begin
						nSlices := 1;
						CurrentSlice := 1;
						PicBaseH[1] := PicBaseHandle;
						SliceSpacing := 0.0;
						FrameInterval := 0.0;
						StackType := VolumeStack;
					end;
				PictureType := NewPicture;
				MakeStackFromWindow := true;
			end;
	end;

	
	procedure SelectSlice (i: integer);
	begin
		with info^, info^.StackInfo^ do
			if i <= nSlices then begin
					hunlock(PicBaseHandle);
					PicBaseHandle := PicBaseH[i];
					hlock(PicBaseHandle);
					{$ifc PowerPC}
					PicBaseAddr := PicBaseHandle^;
					{$elsec}
					PicBaseAddr := StripAddress(PicBaseHandle^);
					{$endc}
					osPort^.PortPixMap^^.BaseAddr := PicBaseAddr;
				end;
	end;


	procedure UpdateWindowsMenuItem;
		var
			str: str255;
			picSize: LongInt;
	begin
		with info^ do begin
			PicSize := PixMapSize;
			if StackInfo <> nil then
				PicSize := PicSize * StackInfo^.nSlices;
			if DataH <> nil then
				PicSize := PicSize + PicSize * SizeOf(real);
			NumToString((PicSize + 511) div 1024, str);
			str := concat(title, '  ', str, 'K');
			SetMenuItemText(WindowsMenuH, PicNum + WindowsMenuItems + nTextWindows, str);
		end;
	end;


	function AddSlice (update: boolean): boolean;
		var
			i: integer;
			h: handle;
			isRoi: boolean;
	begin
		with info^, info^.StackInfo^ do begin
				AddSlice := false;
				if nSlices = MaxSlices then
					exit(AddSlice);
				isRoi := RoiShowing;
				if isRoi then
					KillRoi;
				h := GetBigHandle(PixMapSize);
				if h = nil then begin
						PutError('Not enough memory available to add a slice to this stack.');
						AbortMacro;
						exit(AddSlice);
					end;
				for i := nSlices downto CurrentSlice + 1 do
					PicBaseH[i + 1] := PicBaseH[i];
				nSlices := nSlices + 1;
				CurrentSlice := CurrentSlice + 1;
				PicBaseH[CurrentSlice] := h;
				SelectSlice(CurrentSlice);
				if Update then begin
						SelectAll(false);
						DoOperation(EraseOp);
						UpdatePicWindow;
					end;
				if (StackType = rgbStack) and (nSlices <> 3) then
					StackType := VolumeStack;
				UpdateTitleBar;
				if isRoi then
					RestoreRoi;
				WhatToUndo := NothingToUndo;
				AddSlice := true;
				changes := true;
				PictureType := NewPicture;
				UpdateWindowsMenuItem;
			end;
	end;
	
	
	procedure AbortMacro;
	{If a macro is running, abort it.}
	begin
		macro := false;
	end;
	
	
	procedure TruncateString(var str: str255; len: integer);
	begin
{if length(str) > len then
	beep;}
			if length(str) > len then
			delete(str, len + 1, length(str) - len);
	end;
	
			
	procedure CloseVdig;
	{Closes the current video digitizer component and
	its associated offscreen graphics world.}
	var
		err: osErr;
	begin
		if fgPixMap <> nil then begin
			DisposeGWorld(osGWorld);
			osGWorld := nil;
			GWorldLUT := nil;
			fgPixMap := nil;
		end;
		if vdig <> nil then begin
			err := CloseComponent(vdig);
			vdig := nil;
		end;
		FrameGrabber := noFrameGrabber;
	end;


end.
