unit Camera;

{Routines used by the NIH Image to support Data Translation
and Scion (LG-3, AG-5 or VG-5) frame grabber cards, and
QuickTime compatible digitizers.}

interface


	uses
		Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts, 
		Scrap, ToolUtils, Resources, Errors, Palettes, StandardFile, Windows,
		Controls, TextEdit, Files, Dialogs, TextUtils, Finder, MixedMode, Processes,
		QDOffscreen, Components, QuickTimeComponents, ImageCompression, GestaltEqu, OSUtils,
		globals, Utilities, Graphics, File1, Analysis, Lut;


	function DoAveragingOptions: boolean;
	procedure AverageFrames;
	procedure GetFrame;
	procedure CaptureAndDisplayFrame;
	procedure HighlightPixels;
	procedure ShowTriggerMessage;
	procedure StartDigitizing;
	procedure StopDigitizing;
	function GetFGPixel (h, v: integer): integer;
	procedure WaitForTrigger;
	procedure ShowChannel;
	procedure ShowVideoControl;
	procedure UpdateVideoControl;
	procedure DoVideoControl (item: integer);
	procedure SelectCameraWindow;
	procedure SetOffset (var offset, gain: integer);
	procedure SetGain (var offset, gain: integer);
	procedure ShowOffsetAndGain (offset, gain: integer);
	procedure ShowVideoDialog;
	procedure StartFrame;
	procedure StopFrame;



implementation

type
	IntPtr = ^integer;

var
	SavePicBaseAddr: ptr;
	StopFlagLoc: IntPtr;


procedure GetGrabDepth(var bitDepth: LongInt);
var
	vdigInfo: DigitizerInfo;
begin
	if VDGetDigitizerInfo(vdig, vdigInfo) = noErr then begin
		if DigitizerMode = digitizeGrayscale then begin
			if band(vdigInfo.outputCapabilityFlags, digiOutDoes8) <> 0 then
				bitDepth := 8 {first choice}
			else if band(vdigInfo.outputCapabilityFlags, digiOutDoes32) <> 0 then
				bitDepth := 32 {second choice}
			else if (band(vdigInfo.outputCapabilityFlags, digiOutDoes16) <> 0) then
					bitDepth := 16; {last choice}
		end else begin {capture color}
			if band(vdigInfo.outputCapabilityFlags, digiOutDoes32) <> 0 then
				bitDepth := 32 {first choice}
			else if band(vdigInfo.outputCapabilityFlags, digiOutDoes16) <> 0 then
				bitDepth := 16 {second choice}
			else if (band(vdigInfo.outputCapabilityFlags, digiOutDoes8) <> 0) then
					bitDepth := 8; {last choice}
		end;
	end;
	ShowMessage(StringOf('grab depth=', bitDepth));
end;


procedure SetVideoStandard;
var
	err: ComponentResult;
	inFlags, outFlags: LongInt;
	vdigInfo: DigitizerInfo;
begin
	if VDGetDigitizerInfo(vdig, vdigInfo) <> noErr then
		exit(SetVideoStandard);
	case DigitizerStandard of
		NTSCStd: if band(vdigInfo.inputCapabilityFlags, digiInDoesNTSC) <> 0 then
						err := VDSetInputStandard(vdig, ntscIn);
		PALStd: if band(vdigInfo.inputCapabilityFlags, digiInDoesPAL) <> 0 then
						err := VDSetInputStandard(vdig, palIn);
		SECAMStd: if band(vdigInfo.inputCapabilityFlags, digiInDoesSECAM) <> 0 then
						err := VDSetInputStandard(vdig, secamIn);
		otherwise;
	end;
	err := VDGetCurrentFlags(vdig, inFlags, outFlags);
	if err = noErr then
		if band(inFlags, digiInDoesNTSC) <> 0 then
						DigitizerStandard := NTSCStd
		else if band(inFlags, digiInDoesPAL) <> 0 then
						DigitizerStandard := PALStd
		else if band(inFlags, digiInDoesSECAM) <> 0 then
						DigitizerStandard := SECAMStd;
end;


procedure SetVideoInput;
var
	err: ComponentResult;
	maxChannel, currentChannel: integer;
begin
	err := VDGetNumberOfInputs(vdig, maxChannel);
	if (VideoChannel <= maxChannel) and (err = noErr) then
		err := VDSetInput(vdig, VideoChannel)
	else begin
		VideoChannel := 0;
		err := VDSetInput(vdig, 0);
	end;
	err := VDGetInput(vdig, currentChannel);
	if err = noErr then
		VideoChannel := currentChannel;
end;


function SetupVdig: boolean;
var
	mPtr: MatrixRecordPtr;
	vdErr: ComponentResult;
	vdigInfo: DigitizerInfo;
	DummyMatrixRecord, bitDepth: LongInt;
	err: OSErr;
	flags: GWorldFlags;
	SaveGDevice: GDHandle;
	gwRect, srcRrect: rect;
	str: str255;
begin
   SetupVdig := false;
	SetRect(gwRect, 0, 0, fgWidth, fgHeight);
	bitDepth := 8;
	GetGrabDepth(bitDepth);
	SetVideoInput;
	if bitDepth = 8 then
		vdErr := VDSetInputColorSpaceMode(vdig, 0); {grayscale}
	SaveGDevice := GetGDevice;
	SetGDevice(osGDevice);
	if bitDepth = 8 then
		GWorldLUT := GetCTable(40) {grayscale LUT}
	else
		GWorldLUT := nil;
	flags := 0;
	err := NewGWorld(osGWorld, bitDepth, gwRect, GWorldLUT, nil, flags);
	SetGDevice(SaveGDevice);
	if err <> NoErr then begin
			PutMemoryAlert;
			CloseVdig;
			exit(SetupVdig);
		end;
	fgPixMap := GetGWorldPixMap(osGWorld);
	if not LockPixels(fgPixMap) then begin
			CloseVdig;
			exit(SetupVdig);
		end;
	{err := LockMemoryContiguous(GetPixBaseAddr(fgPixMap), 2097152);}
	vdErr := VDGetActiveSrcRect(vdig, 0, srcRrect);
	if vdErr = noErr then
		vdErr := VDSetDigitizerRect(vdig, srcRrect);
	DummyMatrixRecord := LongInt(nil);
	mPtr := MatrixRecordPtr(ptr(DummyMatrixRecord));
	vdErr := VDSetPlayThruDestination(vdig, fgPixMap, gwRect, MatrixRecord(mPtr^), nil);
	if vdErr = noErr then
		SetupVdig := true
	else begin
		CloseVdig;
		if vdErr = -2208 then
			str := concat(cr, '(Try turning virtual memory or RAM Doubler off.)')
		else
			str := '';
		PutError(StringOf('Video digitizer error ', vdErr, str));
	end;
end;


procedure LookForVDig(var vdigError: boolean);
{Look for a QuickTime video digitizer component}
var
	result: LongInt;
	videoDesc: ComponentDescription;
	srcRrect: rect;
	vdErr: ComponentResult;
	vdigID: Component;
begin
   vdigError := false;
	if Gestalt(gestaltQuickTime, result) <> noErr then begin
		ShowMessage('No QuickTime');
		exit(LookForVDig);
	end;
	{$IFC PowerPC}
	if Gestalt(gestaltQuickTimeFeatures, result) <> noErr then begin
		ShowMessage('No QuickTime PPC support');
		exit(LookForVDig);
	end;
	{$ENDC}
	videoDesc.componentType := VideoDigitizerComponentType;
	videoDesc.componentSubType := OSType(0); {any subtype}
	if UseBuiltinDigitizer then
		videoDesc.componentManufacturer := 'appl'
	else
		videoDesc.componentManufacturer := OSType(0);
	videoDesc.componentFlags := 0;
	videoDesc.componentFlagsMask := 0;
	vdigID :=FindNextComponent(Component(0), videoDesc);
	if vdigID = Component(0) then begin
		videoDesc.componentManufacturer := OSType(0); {any manufacturer}
		vdigID :=FindNextComponent(Component(0), videoDesc);
		if vdigID = Component(0) then begin
			ShowMessage('No vdig found');
			exit(LookForVDig);
		end;
	end;
	vdig := OpenComponent(vdigID);
	if vdig = nil then begin
	  ShowMessage('Unable to open vdig');
	  vdigError := true;
		exit(LookForVDig);
	end;
	SetVideoStandard;
	vdErr := VDGetDigitizerRect(vdig, srcRrect);
	{vdErr := VDGetActiveSrcRect(vdig, 0, srcRrect);}
	if vdErr = noErr then with srcRrect do begin
		fgWidth := (right - left) div fgScale;
		fgHeight := (bottom - top) div fgScale;
	end else begin
		fgWidth := 320;
		fgHeight := 240;
	end;
	FrameGrabber := QTvdig;
	if not SetupVdig then
		vdigError := true;
	HighlightSaturatedPixels := false;
end;


procedure CorrectShadingOfLine (PicPtr, BFPtr: ptr; width, BFMean: integer);
{$IFC PowerPC}
VAR
  PicLine,BFLine:LinePtr;
  i,value:LongInt;
BEGIN
  PicLine:=LinePtr(PicPtr);
  BFLine:=LinePtr(BFPtr);
  FOR i:=0 TO width-1 DO BEGIN
    value:=PicLine^[i];
    value:=255-value;
    value:=(value * BFMean + (BFLine^[i] div 2)) DIV BFLine^[i];
    IF value>254 THEN value:=254;
    IF value<1 THEN value:=1;
    PicLine^[i]:=255-value;
  END;
END;
{$ELSEC}
  {a0=data pointer}
  {a1=blank field data pointer}
  {d0=count}
  {d1=pixel value}
  {d2=blank field pixel value}
  {d3=blank field mean}
  {d4=temp}
  {d5=max pixel value(245)}
  {d6=min pixel value(1)}
	inline
		$4E56, $0000, {   link	a6,#0}
		$48E7, $FEC0, {   movem.l	a0-a1/d0-d6,-(sp)}
		$206E, $000C, {   move.l	12(a6),a0}
		$226E, $0008, {   move.l	8(a6),a1}
		$4280,       {   clr.l	d0}
		$302E, $0006, {   move.w	6(a6),d0}
		$362E, $0004, {   move.w	4(a6),d3}
		$2A3C, $0000, $00FE, {   move.l	#254,d5}
		$2C3C, $0000, $0001, {   move.l	#1,d6}
		$5380,       {   subq.l	#1,d0}
		$4281,       {   clr.l	d1}
		$4282,       {   clr.l	d2}
		$1210,       {L1	move.b	(a0),d1}
		$1419,       {   move.b	(a1)+,d2}
		$4601,       {   not.b	d1}
		$C2C3,       {   mulu.w	d3,d1}
		$2802,       {   move.l	d2,d4}
		$E244,       {   asr.w	#1,d4}
		$D284,       {   add.l	d4,d1}
		$82C2,       {   divu.w	d2,d1}
		$B245,       {   cmp.w	d5,d1}
		$6F02,       {   ble.s	L2}
		$3205,       {   move.w	d5,d1}
		$B246,       {L2	cmp.w	d6,d1}
		$6C02,       {   bge.s	L3}
		$3206,       {   move.w	d6,d1}
		$4601,       {L3	not.b	d1}
		$10C1,       {   move.b	d1,(a0)+}
		$51C8, $FFDE, {   dbra	d0,L1}
		$4CDF, $037F, {   movem.l	(sp)+,a0-a1/d0-d6}
		$4E5E,       {   unlk	a6}
		$DEFC, $000C; {   add.w	#12,sp}
{$ENDC}


	procedure CorrectShading;
		var
			i, tag, width: integer;
			offset, NextUpdate: LongInt;
			p1, p2: ptr;
			str: str255;
			MaskRect:rect;
	begin
		with info^ do begin
				if ImageSize <> BlankFieldInfo^.ImageSize then begin
						beep;
						exit(CorrectShading);
					end;
				ShowWatch;
				tag:=0;
				NextUpdate:=TickCount+6;
				width:=PicRect.right;
				p1 := PicBaseAddr;
				p2 := BlankFieldInfo^.PicBaseAddr;
				for i := 1 to nLines do begin
						CorrectShadingOfLine(p1, p2, PixelsPerLine, BlankFieldMean);
						p1 := ptr(ord4(p1) + info^.BytesPerRow);
						p2 := ptr(ord4(p2) + BlankFieldInfo^.BytesPerRow);
					if TickCount>=NextUpdate then begin
							SetRect(MaskRect, 0, tag, width, i);
							UpdateScreen(MaskRect);
							tag:=i;
							NextUpdate:=TickCount+6;
						end;
					end;
				SetRect(MaskRect, 0, tag, width, nLines);
				UpdateScreen(MaskRect);
				str := title;
				if SpatiallyCalibrated then
					str := concat(str, chr($13)); {Black Diamond}
				if fit <> uncalibrated then
					str := concat(str, '');
				if wptr <> nil then
					SetWTitle(wptr, concat(str, ' (Corrected)'));
			end;
	end;


	procedure CopyVdigImageOffscreen;
	var
		SaveExtraColors: integer;
	begin
		with info^ do begin
				SaveExtraColors := 0;
				if (LUTMode = Grayscale) and (not IdentityFunction or (nExtraColors <> 0)) then begin
					SaveExtraColors := nExtraColors;
					nExtraColors := 0;
					ResetGrayMap;
				end;
				CopyOffscreen(fgPixMap, osPort^.portPixMap, PicRect, PicRect);
				if SaveExtraColors <> 0 then begin
					nExtraColors := SaveExtraColors;
					LoadLUT(ctable);
				end;
				UpdatePicWindow;
		end; {with}
	end;
	
	
	procedure StartFrame;
	begin
		if CurrentBufferIsZero then begin
			if FrameGrabber = ScionAG5 then
				BufferReg^ := $81
			else
				BufferReg^ := 0
		end else begin
			if FrameGrabber = ScionAG5 then
				BufferReg^ := $89
			else
				BufferReg^ := 1;
		end;
		if ExternalTrigger then begin
			if FrameGrabber = ScionAG5 then
				ControlReg^ := bor(bor(ord(AG5GrabMode), $90), bsl(ord(AG5LutMode and not ControlKeyDown), 2))
			else
				ControlReg^ := $90 {Start frame capture}
		end else begin
			if FrameGrabber = ScionAG5 then
				ControlReg^ := bor(bor(ord(AG5GrabMode), $80), bsl(ord(AG5LutMode and not ControlKeyDown), 2))
			else
				ControlReg^ := $80; {Start frame capture}
		end;
	end;


	procedure StopFrame;
		var
			ticks, timeout: LongInt;
	begin
		if ExternalTrigger then begin {Wait for trigger}
				repeat
					if button then
						ExternalTrigger := false;
				until (BitAnd(ControlReg^, $80) = $80) or not ExternalTrigger;
				ControlReg^ := 0;
			end {if External Trigger}
		else begin
				TimeOut := TickCount + 30;  {1/2sec. timeout}
				while BitAnd(ControlReg^, $80) = 0 do begin    {Wait for it to complete}
						if TickCount > TimeOut then begin
								ControlReg^ := 0;
								leave
							end;
					end;
				ControlReg^ := 0;
			end;
		with fgPort^ do
			with PortPixMap^^ do
				if CurrentBufferIsZero then
					BaseAddr := ptr(fgSuperSlotBase0)
				else
					BaseAddr := ptr(fgSuperSlotBase1);
		CurrentBufferIsZero := not CurrentBufferIsZero;
		fgFrameCount := fgFrameCount + 1;
	end;


	procedure StopDigitizing;
	begin
		if digitizing then
			with info^ do begin
					ShowFrameRate('', fgStartTicks, fgFrameCount);
					if vdig <> nil then
						CopyVdigImageOffscreen
					else
						CopyOffscreen(fgPixMap, osPort^.portPixMap, PicRect, PicRect);
					SetMenuItemText(SpecialMenuH, StartItem, 'Start Capturing');
					Digitizing := false;
					ContinuousHistogram := false;
					if DoubleBuffering then begin
						StopFrame;
						BufferReg^ := 0;
						CurrentBufferIsZero := true;
						DoubleBuffering := false;
						with fgPort^ do
							with PortPixMap^^ do
								BaseAddr := ptr(fgSuperSlotBase0)
					 end;
					with info^ do
						if PictureType = FrameGrabberType then begin
								title := 'Camera';
								UpdateTitleBar;
								if HighlightSaturatedPixels then
									LoadLUT(ctable);
							end;
					if (ScreenDepth<>8) and HighlightSaturatedPixels then
						UpdatePicWindow;
					if (BlankFieldInfo <> nil) and not OptionKeyDown then
						CorrectShading;
				end;
	end;


	procedure GetFrame;
		var
			ticks, timeout: LongInt;
			temp:integer;
			vdigErr: ComponentResult;
	begin
			case FrameGrabber of
			
				ScionLG3, ScionVG5f:
					if ExternalTrigger then begin {Wait for trigger}
							ControlReg^ := $90;
							repeat
								if button then
									ExternalTrigger := false;
							until (band(ControlReg^, $80) = $80) or not ExternalTrigger;
							ControlReg^ := 0;
							if Digitizing then
								StopDigitizing;
							UpdateVideoControl;
						end {if External Trigger}
					else begin
							TimeOut := TickCount + 30;  {1/2sec. timeout}
							ControlReg^ := $80; {Start frame capture}
							while band(ControlReg^, $80) = 0 do begin    {Wait for it to complete}
									if TickCount > TimeOut then begin
											ControlReg^ := 0;
											leave
										end;
								end;
							ControlReg^ := 0;
						end;
				
			ScionAG5:
					if ExternalTrigger then begin {Wait for trigger}
							ControlReg^ := bor(bor(ord(AG5GrabMode), $90), bsl(ord(AG5LutMode and not ControlKeyDown), 2));
							repeat
								if button then
									ExternalTrigger := false;
							until (band(ControlReg^, $80) = $80) or not ExternalTrigger;
							ControlReg^ := 0;
							if Digitizing then
								StopDigitizing;
							UpdateVideoControl;
						end {if External Trigger}
					else begin
							TimeOut := TickCount + 30;  {1/2sec. timeout}
							ControlReg^ := bor(bor(ord(AG5GrabMode), $80), bsl(ord(AG5LutMode and not ControlKeyDown), 2)); {Start frame capture}
							repeat
								if TickCount > TimeOut then
									leave;
								temp:=ControlReg^; {ppc-bug}
							until band(temp, $80) <> 0; {Wait for it to complete}
							ControlReg^ := 0;
						end;
						
			QuickCapture:
				if ExternalTrigger then begin {Wait for trigger}
						ControlReg^ := $82; {Set Busy and External Trigger Enable bits}
						repeat
							if button then
								ExternalTrigger := false;
							temp:=ControlReg^; {ppc-bug}
						until (band(temp, $80) = 0) or not ExternalTrigger;
						if Digitizing then
							StopDigitizing;
						UpdateVideoControl;
					end {if External Trigger}
				else begin
						TimeOut := TickCount + 30;  {1/2sec. timeout}
						ControlReg^ := $80; {Start frame capture by setting busy bit}
						repeat
							if TickCount > TimeOut then
								leave;
							temp:=ControlReg^; {ppc-bug}
						until band(temp, $80) = 0; {Wait for frame capture to complete}
					end;
			
			QTvdig: begin
							if ExternalTrigger then begin {Wait for mouse press}
								repeat
								until button;
								ExternalTrigger := false;
							end;
							if vdig <> nil then
								vdigErr := VDGrabOneFrame(vdig);
					end;
				
		end; {case}
		fgFrameCount := fgFrameCount + 1;
	end;


	procedure CaptureAndDisplayFrame;
		var
			tPort: GrafPtr;
			SaveGDevice: GDHandle;
	begin
		with info^ do begin
				if (PictureType <> FrameGrabberType) or (PixelsPerLine <> fgWidth) or (nlines <> fgHeight) then begin
						Digitizing := false;
						exit(CaptureAndDisplayFrame);
					end;
				if DoubleBuffering then begin
					StopFrame;
					StartFrame;
				 end else
					GetFrame;
				SaveGDevice := GetGDevice;
				SetGDevice(GetMainDevice);
				getPort(tPort);
				SetPort(wptr);
				SetFColor(BlackIndex);
				SetBColor(WhiteIndex);
				if (FrameGrabber = QTvdig) and (LUTMode <> grayscale) and (ScreenDepth <= 8) then
					CopyBits(BitMapHandle(fgPixMap)^^, BitMapHandle(CGrafPtr(wptr)^.PortPixMap)^^, SrcRect, wrect, ditherCopy, nil)
				else
					CopyBits(BitMapHandle(fgPixMap)^^, BitMapHandle(CGrafPtr(wptr)^.PortPixMap)^^, SrcRect, wrect, srcCopy, nil);
				SetPort(tPort);
				SetGDevice(SaveGDevice);
			end;
	end;


	procedure SetReg (index, value: integer);
		const
			RegOffset = $f5fe0;
		var
			reg: ptr;
	begin
		reg := ptr(fgSlotBase + RegOffset + index * 4);
		reg^ := value;
	end;


	{$ifc PowerPC} {ppc-bug}
	procedure SwapMMUMode(var mode:SignedByte);
	begin
	end;
	{$endc}
	
	
	procedure SelectCameraWindow;
  {If there is a Camera window, activate it, otherwise, do nothing.}
		var
			i: integer;
			TempInfo: InfoPtr;
	begin
		for i := 1 to nPics do begin
				TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
				if TempInfo^.PictureType = FrameGrabberType then begin
						if PicWindow[i] <> nil then begin
								if OpPending then
									KillRoi;
								SelectWindow(PicWindow[i]);
								Info := TempInfo;
								ActivateWindow;
							end; {if}
						leave;
					end; {if}
			end; {for}
	end;


	procedure HighlightPixels;
		var
			lut: MyCSpecArray;
	begin
		with info^ do begin
				lut := ctable;
				lut[1].rgb := Highlight1;
				lut[254].rgb := Highlight254;
				LoadLUT(lut);
			end;
	end;


	procedure ShowTriggerMessage;
	begin
		if ExternalTrigger and (frameGrabber <> noFrameGrabber) then
			ShowMessage(concat('EXTERNAL TRIGGER MODE', crStr, '(Press mouse button to exit)'));
	end;


	procedure StartDigitizing;
		var
			i, width, height: integer;
			trect: rect;
			NewWindow: boolean;
			vdigError: boolean;
	begin
		if FrameGrabber = NoFrameGrabber then begin
			LookForVDig(vdigError);
			if vdigError then
				exit(StartDigitizing);
		end;
		if FrameGrabber = NoFrameGrabber then begin
				PutError('Capturing requires a Data Translation, Scion or QuickTime compatible frame grabber.');
				AbortMacro;
				exit(StartDigitizing)
			end;
		if Digitizing then begin
				StopDigitizing;
				if BlankFieldInfo <> nil then
					wait(15);
				FlushEvents(EveryEvent, 0); {In case user holds key down too long}
				exit(StartDigitizing)
			end;
		if info^.PictureType <> FrameGrabberType then
			SelectCameraWindow;
		NewWindow := false;
		with info^ do
			if (PictureType <> FrameGrabberType) or (PixelsPerLine <> fgWidth) or (nlines <> fgHeight) then begin
					if not NewPicWindow('Camera', fgWidth, fgHeight) then
						exit(StartDigitizing);
					if FrameGrabber = QTvdig then with info^ do begin
						fgPort := osPort;
						fgSlotBase := LongInt(PicBaseAddr);
						fgRowBytes := BytesPerRow;
					end;
					NewWindow := true;
				end;
		with info^ do begin
				PictureType := FrameGrabberType;
				if NewWindow and (not EqualRect(SrcRect, PicRect)) then {Center Frame}
					with SrcRect do begin
							width := right - left;
							height := bottom - top;
							left := (PicRect.right - width) div 2;
							right := left + width;
							top := (PicRect.bottom - height) div 2;
							bottom := top + height;
						end;
				KillRoi;
				if ScaleToFitWindow then
					ScaleToFit;
				with SrcRect do begin
						width := right - left;
						left := band(left, $fffc);
						right := left + width;
					end;
				GetWindowRect(wptr, trect);
				with trect do
					if band(left, 3) <> 0 then
						MoveWindow(wptr, band(left, $fffc), top, true); {Forces window to be word aligned}
				with SrcRect do {Prevents bus errors when Camera window moved.}
					if (top = 0) and (bottom < PicRect.bottom) then begin
							top := top + 1;
							bottom := bottom + 1;
						end;
				ResetFrameGrabber;
				Digitizing := true;
				SetMenuItemText(SpecialMenuH, StartItem, 'Stop Capturing');
				changes := true;
				BinaryPic := false;
				UpdateTitleBar;
				if HighlightSaturatedPixels then
					HighlightPixels;
			end; {with info}
		fgFrameCount := 0;
		fgStartTicks := TickCount;
		ContinuousHistogram := false;
		ShowTriggerMessage;
		if PCIFramegrabber and not ExternalTrigger then begin
			DoubleBuffering := true;
			CurrentBufferIsZero := true;
			StartFrame;
		end;
	end;


	procedure AddLineToSum (src, dst: ptr; width: LongInt);
{$IFC PowerPC}
		type
			SumLineType = array[0..2047] of integer;
			fptr = ^SumLineType;
		var
			FrameLine: LinePtr;
			SumLine: fptr;
			i: integer;
	begin
		FrameLine := LinePtr(src);
		SumLine := fptr(dst);
		for i := 0 to width - 1 do
			SumLine^[i] := SumLine^[i] + FrameLine^[i];
	end;
{$ELSEC}
inline
{a0=data pointer}
{a1=sum buffer pointer}
{d0=count}
{d1=pixel value}
{d2=temp}
	$4E56, $0000, {link	a6,#0}
	$48E7, $E0C0, {movem.l	a0-a1/d0-d2,-(sp)}
	$206E, $000C, {move.l	12(a6),a0}
	$226E, $0008, {move.l	8(a6),a1}
	$202E, $0004, {move.l	4(a6),d0}
	$5380,              {subq.l	#1,d0}
	$4281,              {clr.l	d1}
	$4282,              {clr.l	d2}
	$1218,              {L1	move.b	(a0)+,d1}
	$3411,              {move.w	(a1),d2}
	$D441,              {add.w  	d1,d2}
	$32C2,              {move.w	d2,(a1)+}
	$51C8, $FFF6, {dbra	d0,L1}
	$4CDF, $0307, {movem.l	(sp)+,a0-a1/d0-d2}
	$4E5E,               {unlk	a6}
	$DEFC, $000C; {add.w	#12,sp}
{$ENDC}


function DoAveragingOptions: boolean;
	const
		FramesID = 8;
		VideoRateID = 9;
		SumID = 10;
		ShowID = 11;
		FixID = 12;
		MinID = 13;
		MaxID = 14;
		OnChipID = 15;
	var
		mylog: DialogPtr;
		item, i: integer;
begin
	InitCursor;
	mylog := GetNewDialog(140, nil, pointer(-1));
	if not SumFrames then begin
			ShowIntegratedValues := false;
			FixIntegrationScale := false;
		end;
	SetDNum(MyLog, FramesID, FramesToAverage);
	SetDlogItem(mylog, SumID, ord(SumFrames));
	SetDlogItem(mylog, VideoRateID, ord(VideoRateAveraging));
	SetDlogItem(mylog, ShowID, ord(ShowIntegratedValues));
	SetDlogItem(mylog, FixID, ord(FixIntegrationScale));
	SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip));
	SetDNum(MyLog, MinID, IntegrationMin);
	SetDNum(MyLog, MaxID, IntegrationMax);
	SelectDialogItemText(MyLog, FramesID, 0, 32767);
	repeat
		ModalDialog(nil, item);
		if item = FramesID then
			FramesToAverage := GetDNum(MyLog, FramesID);
		if item = SumID then begin
				SumFrames := not SumFrames;
				if SumFrames then
					IntegrateOnChip := false
				else begin
						FixIntegrationScale := false;
						ShowIntegratedValues := false;
					end;
				SetDlogItem(mylog, SumID, ord(SumFrames));
				SetDlogItem(mylog, FixID, ord(FixIntegrationScale));
				SetDlogItem(mylog, ShowID, ord(ShowIntegratedValues));
				SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip));
			end;
		if item = VideoRateID then begin
				VideoRateAveraging := not VideoRateAveraging;
				if VideoRateAveraging then
					IntegrateOnChip := false;
				SetDlogItem(mylog, VideoRateID, ord(VideoRateAveraging));
				SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip));
			end;
		if item = ShowID then begin
				ShowIntegratedValues := not ShowIntegratedValues;
				if ShowIntegratedValues then begin
						SumFrames := true;
						IntegrateOnChip := false;
					end;
				SetDlogItem(mylog, ShowID, ord(ShowIntegratedValues));
				SetDlogItem(mylog, SumID, ord(SumFrames));
				SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip));
			end;
		if item = FixID then begin
				FixIntegrationScale := not FixIntegrationScale;
				if FixIntegrationScale then begin
						SumFrames := true;
						IntegrateOnChip := false;
					end;
				SetDlogItem(mylog, FixID, ord(FixIntegrationScale));
				SetDlogItem(mylog, SumID, ord(SumFrames));
				SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip));
			end;
		if (item = MinID) or (item = MaxID) then begin
				if item = MinID then
					IntegrationMin := GetDNum(MyLog, MinID)
				else
					IntegrationMax := GetDNum(MyLog, MaxID);
				SumFrames := true;
				FixIntegrationScale := true;
				IntegrateOnChip := false;
				SetDlogItem(mylog, SumID, ord(SumFrames));
				SetDlogItem(mylog, FixID, ord(FixIntegrationScale));
				SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip));
			end;
		if item = OnChipID then begin
				IntegrateOnChip := not IntegrateOnChip;
				if IntegrateOnChip then begin
						SumFrames := false;
						VideoRateAveraging := false;
						FixIntegrationScale := false;
						ShowIntegratedValues := false;
					end;
				SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip));
				SetDlogItem(mylog, SumID, ord(SumFrames));
				SetDlogItem(mylog, VideoRateID, ord(VideoRateAveraging));
				SetDlogItem(mylog, FixID, ord(FixIntegrationScale));
				SetDlogItem(mylog, ShowID, ord(ShowIntegratedValues));
			end;
	until (item = ok) or (item = cancel);
	DisposeDialog(mylog);
	if FramesToAverage < 2 then
		FramesToAverage := 2;
	if IntegrationMin < 0 then
		IntegrationMin := 0;
	if IntegrationMax > 32767 then
		IntegrationMax := 32767;
	if VideoRateAveraging and (item <> cancel) then begin
			if (FrameGrabber <> ScionLG3) and (FrameGrabber <> ScionAG5) then begin
					VideoRateAveraging := false;
					PutError('Video rate averaging or summation requires a Scion LG-3 or a Scion AG-5.');
					DoAveragingOptions := false;
					exit(DoAveragingOptions);
				end;
			if (FrameGrabber = ScionLG3) and (FramesToAverage > MaxLG3Frames) then begin
					FramesToAverage := MaxLG3Frames;
					DoAveragingOptions := false;
					PutError(concat('This ', long2str(MaxLG3Frames div 2), 'MB LG-3 can capture a maximum of ', long2str(MaxLG3Frames), ' frames at video rates.'));
					exit(DoAveragingOptions);
				end;
			if (FrameGrabber = ScionAG5) and (FramesToAverage > 127) then begin
					FramesToAverage := 127;
					DoAveragingOptions := false;
					PutError(concat('The AG-5 can average or sum a maximum of 127 frames at video rates.'));
					exit(DoAveragingOptions);
				end;
		end;
	if IntegrateOnChip and (item <> cancel) then
		if (FrameGrabber <> ScionLG3) and (FrameGrabber <> ScionAG5) and (FrameGrabber <> ScionVG5f)  then begin
				IntegrateOnChip := false;
				PutError('On-chip integration requires a Scion frame grabber.');
				DoAveragingOptions := false;
				exit(DoAveragingOptions);
			end;
	DoAveragingOptions := item <> cancel;
end;



function OddEven: boolean;
{Looks at the the Field Status bit of the Status Register,
which has the same address as Control Register 1. This bit is
high during the odd field and low during the even field.}
begin
 if band(ControlReg^, $10) = $10 then
  OddEven := true
 else
  OddEven := false;
end;


procedure WaitForOdd;
 var
  timeout: LongInt;
begin
 TimeOut := TickCount + 30;  {1/2sec. timeout}
 while OddEven do
  if TickCount > TimeOut then
   Exit(WaitForOdd);
 TimeOut := TickCount + 30;  {1/2sec. timeout}
 while not OddEven do
  if TickCount > TimeOut then
   Exit(WaitForOdd);
end;


procedure IntegrateOn;
{Sets bit 3 (Open Drain Output) of Control Register 1 high
which pulls pin 11 of the 15 pin connector low, causing the
Cohu camera to start integrating.}
begin
 ControlReg^ := $08;
end;


procedure IntegrateOff;
{Sets bit 3 of Control Register 1 low which open circuits
 pin 11, causing the Cohu camera to stop integrating.}
begin
 ControlReg^ := $00;
end;


procedure DoOnChipIntegration;
{Requires a Scion LG-3, a Cohu 4910 series camera, and a cable available from Scion.}
var
	i,StartTicks:LongInt;
	str:str255;
begin
	WaitForOdd;
	IntegrateOn;
	StartTicks := TickCount;
	for i := 1 to FramesToAverage - 1 do begin
		WaitForOdd;
		if (i mod 30) = 0 then
			ShowAnimatedWatch;
		if CommandPeriod then
			leave;
	end;
	IntegrateOff;
	GetFrame;
	RealToString((TickCount - StartTicks) / 60.0, 1, 2, str);
	ShowFrameRate(concat(Long2str(FramesToAverage), ' frames', cr, str, ' seconds', cr), StartTicks, FramesToAverage);
	with info^ do
		CopyOffscreen(fgPixMap, osPort^.portPixMap, RoiRect, RoiRect);
	UpdatePicWindow;
	KillRoi;
	if BlankFieldInfo <> nil then
		CorrectShading;
	if info^.fit<>uncalibrated then
		RemoveDensityCalibration;
end;


procedure DoHardwareAveraging;
{Do averaging or integration at video rates using the Scion Ag-5.}
var
  StartTicks,ActualMin,ActualMax:LongInt;
  str1,str2:str255;
  frame,i:integer;
  roi:rect;
begin
	roi:=info^.RoiRect;
	KillRoi;
	if FramesToAverage > 127 then
		FramesToAverage := 127;
	ExternalTrigger := false;
	AG5GrabMode := GrabNormal;
	GetFrame;
	StartTicks := TickCount;
	AG5GrabMode := GrabSum;
	for frame := 1 to FramesToAverage - 1 do begin
			GetFrame;
		end;
	RealToString((TickCount - StartTicks) / 60.0, 1, 2, str2);
	if not SumFrames then begin
			ConstantReg^ := FramesToAverage;
			AG5GrabMode := GrabDivide;
			GetFrame;
			AG5GrabMode := GrabNormal;
			str1 := '';
		end
	else begin
			ActualMin := Ord4(ScaleLowReg^);
			ActualMax := Ord4(ScaleHighReg^);
			if FixIntegrationScale then begin
					ScaleLowReg^ := integer(IntegrationMin);
					ScaleHighReg^ := integer(IntegrationMax);
				end;
			AG5GrabMode := GrabScale;
			GetFrame;
			AG5GrabMode := GrabNormal;
			if FixIntegrationScale then
				str1 := concat('min=', long2str(IntegrationMin), ' (', long2str(ActualMin), ')', cr, 'max=', long2str(IntegrationMax), ' (', long2str(ActualMax), ')', cr)
			else
				str1 := concat('min=', long2str(ActualMin), cr, 'max=', long2str(ActualMax), cr)
		end;
	ShowFrameRate(concat(Long2str(FramesToAverage), ' frames', cr, str1, str2, ' seconds', cr), StartTicks, FramesToAverage);
	with info^ do
		CopyOffscreen(fgPixMap, osPort^.portPixMap, roi, roi);
	UpdatePicWindow;
	if not EqualRect(roi, info^.PicRect) then
		RestoreRoi;
	if BlankFieldInfo <> nil then
		CorrectShading;
	if ShowIntegratedValues then with info^ do begin
			fit := StraightLine;
			nCoefficients := 2;
			coefficient[2] := (ActualMax - ActualMin) / 253.0;
			coefficient[1] := ActualMin - coefficient[2];
			ZeroClip := false;
			UpdateTitleBar;
			if macro then
				GenerateValues;
		end else
			if SumFrames and (info^.fit<>uncalibrated) then
				RemoveDensityCalibration;
	end; {DoAG5HardwareAveraging}
	

procedure AverageFrames;
	type
		IntPtr = ^integer;
		SumLineType = array[0..2047] of integer;
		sptr = ^SumLineType;
	var
		AutoSelectAll: boolean;
		SelectionSize, FrameBufferSize, offset, StartTicks: LongInt;
		SumBase, src, srcbase, dst, OffscreenBase: ptr;
		str1, str2: str255;
		xLines, xPixelsPerLine, BytesPerLine, frame, line, pixel: integer;
		aline, BlankLine: LineType;
		GrabRect: rect;
		hstart, vstart, wwidth, wheight: integer;
		j, FramesAveraged: integer;
		SrcRowBytes, DstRowBytes, i, value, MinV, MaxV, range, ActualMin, ActualMax: LongInt;
		iptr: IntPtr;
		FrameLine: LinePtr;
		SumLine: sptr;
		SaveBlankFieldInfo: InfoPtr;
		myMMUMode: signedbyte;
begin
	with info^ do
		if PictureType <> FrameGrabberType then begin
				PutError('You must have an active Camera window (created using Start Capturing) in order to average frames.');
				AbortMacro;
				exit(AverageFrames)
			end;
	if NotRectangular or NotinBounds then begin
			AbortMacro;
			exit(AverageFrames);
		end;
	if (not OptionKeyWasDown) and (not macro) then begin
			if not DoAveragingOptions then
				exit(AverageFrames);
		end;
	SaveBlankFieldInfo := BlankFieldInfo;
	BlankFieldInfo := nil; {We don't want to do shading correction now}
	StopDigitizing;
	BlankFieldInfo := SaveBlankFieldInfo;
	OptionKeyWasDown := false;
	if (FrameGrabber <> ScionLG3) and (FrameGrabber <> ScionAG5) then
		VideoRateAveraging := false;
	if (FrameGrabber <> ScionLG3) and (FrameGrabber <> ScionAG5) and (FrameGrabber <> ScionVG5f) then
		IntegrateOnChip := false;
	ShowWatch;
	ShowTriggerMessage;
	AutoSelectAll := not Info^.RoiShowing;
	if AutoSelectAll then
		SelectAll(false);
	WhatToUndo := NothingToUndo;
	ContinuousHistogram := false;
	ResetFrameGrabber;
	if IntegrateOnChip then begin
		DoOnChipIntegration;
		exit(AverageFrames);
	end;
	if VideoRateAveraging and (FrameGrabber=ScionAg5) then begin
		DoHardwareAveraging;
		exit(AverageFrames);
	end;
	DrawLabels('Frame:', 'Total:', '');
	with info^.RoiRect do
		SelectionSize := (ord4(right) - left) * (bottom - top);
	FrameBufferSize := SelectionSize * 2;
	if FrameBufferSize > BigBufSize then begin
			NumToString(FrameBufferSize div 1024, str1);
			NumToString(BigBufSize div 1024, str2);
			str2 := concat(str1, 'K bytes are required, but only ', str2, 'K bytes are available.');
			PutError(concat('There is not enough memory to do the requested frame averaging. ', str2));
			if AutoSelectAll or (BlankFieldInfo <> nil) then
				KillRoi
			else
				ShowRoi;
			exit(AverageFrames)
		end;
	WhatsOnClip := NothingOnClip;
	SumBase := BigBuf;
	with info^, info^.RoiRect do begin
			offset := left + ord4(top) * BytesPerRow;
			OffscreenBase := ptr(ord4(PicBaseAddr) + offset);
			offset := left + ord4(top) * fgRowBytes;
			srcbase := ptr(ord4(ptr(fgSlotBase)) + offset);
			SrcRowBytes := fgRowBytes;
			xLines := bottom - top;
			xPixelsPerLine := right - left;
			BytesPerLine := xPixelsPerLine * 2;
		end; {with}
	for i := 0 to BytesPerLine - 1 do
		BlankLine[i] := WhiteIndex;
	dst := SumBase;
	for line := 1 to xLines do begin {zero buffer}
			BlockMove(@BlankLine, dst, BytesPerLine);
			dst := ptr(ord4(dst) + BytesPerLine);
		end;
	info^.title := 'Camera';
	UpdateTitleBar;
	StartTicks := TickCount;
	if VideoRateAveraging then begin
			if FramesToAverage > MaxLG3Frames then
				FramesToAverage := MaxLG3Frames;
			ExternalTrigger := false;
			BufferReg^ := 0;
			GetFrame;
			StartTicks := TickCount - 2;
			for frame := 1 to FramesToAverage - 1 do begin
					BufferReg^ := Frame;
					GetFrame;
				end;
			BufferReg^ := 0;
			RealToString((TickCount - StartTicks) / 60.0, 1, 2, str1);
			ShowFrameRate(concat(Long2str(FramesToAverage), ' frames', crStr, str1, ' seconds', crStr), StartTicks, FramesToAverage);
		end; {if VideoRateAveraging}
	for frame := 0 to FramesToAverage - 1 do begin
			Show2Values(frame + 1, FramesToAverage);
			if VideoRateAveraging then
				BufferReg^ := Frame
			else begin
				GetFrame;
				if FrameGrabber = QTvdig then with info^ do
				CopyOffScreen(fgPixMap, osPort^.portPixMap, roiRect, roiRect);
			end;
			src := srcbase;
			dst := SumBase;
			myMMUMode := 1;
			SwapMMUMode(myMMUMode);
			for line := 1 to xLines do begin
					AddLineToSum(src, dst, xPixelsPerLine);
					src := ptr(ord4(src) + SrcRowBytes);
					dst := ptr(ord4(dst) + BytesPerLine);
				end;
			SwapMMUMode(myMMUMode);
			if CommandPeriod then begin
					beep;
					if AutoSelectAll then
						KillRoi
					else
						ShowRoi;
					exit(AverageFrames);
				end;
		end; {for}
	src := SumBase;
	dst := OffscreenBase;
	DstRowBytes := info^.BytesPerRow;
	if SumFrames then begin
			MinV := 2000000000;
			MaxV := 0;
			iptr := IntPtr(src);
			for i := 1 to SelectionSize do begin
					value := iptr^;
					if value > MaxV then
						MaxV := value;
					if value < MinV then
						MinV := value;
					iptr := IntPtr(ord4(iptr) + 2);
				end;
			ActualMin := MinV;
			ActualMax := MaxV;
			if FixIntegrationScale then begin
					MinV := IntegrationMin;
					MaxV := IntegrationMax;
				end;
			range := MaxV - MinV;
			if range <> 0 then
				for line := 1 to xLines do begin
						SumLine := sptr(src);
						FrameLine := LinePtr(dst);
						for j := 0 to xPixelsPerLine - 1 do begin
								value := ord4(SumLine^[j] - MinV) * 253 div range + 1;
								if value < 0 then
									value := 0;
								if value > 255 then
									value := 255;
								FrameLine^[j] := value;
							end;
						src := ptr(ord4(src) + BytesPerLine);
						dst := ptr(ord4(dst) + DstRowBytes);
					end
			else
				beep;
		end
	else
		for line := 1 to xLines do begin
				SumLine := sptr(src);
				FrameLine := LinePtr(dst);
				for j := 0 to xPixelsPerLine - 1 do
					FrameLine^[j] := SumLine^[j] div FramesToAverage;
				src := ptr(ord4(src) + BytesPerLine);
				dst := ptr(ord4(dst) + DstRowBytes);
			end;
	if not VideoRateAveraging then begin
			if SumFrames then begin
					if FixIntegrationScale then
						str1 := concat('min=', long2str(MinV), ' (', long2str(ActualMin), ')', crStr, 'max=', long2str(MaxV), ' (', long2str(ActualMax), ')', crStr)
					else
						str1 := concat('min=', long2str(MinV), crStr, 'max=', long2str(MaxV), crStr)
				end
			else
				str1 := '';
			RealToString((TickCount - StartTicks) / 60.0, 1, 2, str2);
			ShowFrameRate(concat(Long2str(FramesToAverage), ' frames', crStr, str1, str2, ' seconds', crStr), StartTicks, FramesToAverage);
		end;
	UpdatePicWindow;
	if AutoSelectAll then
		KillRoi
	else
		ShowRoi;
	if BlankFieldInfo <> nil then
		CorrectShading;
	if ShowIntegratedValues then with info^ do begin
			fit := StraightLine;
			nCoefficients := 2;
			coefficient[2] := (MaxV - MinV) / 253.0;
			coefficient[1] := MinV - coefficient[2];
			nKnownValues := 0;
			ZeroClip := false;
			UpdateTitleBar;
			if macro then
				GenerateValues;
		end else
			if SumFrames and (info^.fit<>uncalibrated) then
				RemoveDensityCalibration;
end;


function GetFGPixel (h, v: integer): integer;
	var
		offset: LongInt;
		p: ptr;
begin
	if FrameGrabber = QTvdig then begin
		GetFGPixel := 0;
		exit(GetFGPixel);
	end;
	with Info^ do begin
			if (h < 0) or (v < 0) or (h >= fgWidth) or (v >= fgHeight) then begin
					GetFGPixel := WhiteIndex;
					exit(GetFGPixel);
				end;
			offset := ord4(v) * fgRowBytes + h;
			if offset >= ord4(fgHeight) * fgRowBytes then begin
					GetFGPixel := WhiteIndex;
					exit(GetFGPixel);
				end;
				p := ptr(ord4(ptr(fgSlotBase)) + offset);
			GetFGPixel := BAND(p^, 255);
		end;
end;


procedure WaitForTrigger;
begin
	StopDigitizing;
	ShowWatch;
	case FrameGrabber of
		QuickCapture:  begin
				ControlReg^ := BitAnd($82, 255);  {Wait for external trigger and capture one frame}
				repeat
				until (BitAnd(ControlReg^, $80) = $00) or Button;  {Wait for it to complete}
			end;
		ScionLG3, ScionAg5, ScionVG5f:  begin
				ControlReg^ := $90; {Wait for external trigger and capture one frame}
				repeat
				until (BitAnd(ControlReg^, $80) = $80) or Button;  {Wait for it to complete}
			end;
		otherwise
			repeat
			until Button;
	end;
end;


procedure DoVideoSettingsDialog;
{Displays QuickTime video digitizer options dialog box}
	const
		grayID = 6;
		color8ID = 7;
		color24ID = 8;
		fullID = 10;
		oneHalfID = 11;
		oneQuarterID = 12;
		ntscID = 14;
		palID = 15;
		secamID =16;
		builtinID = 17;
		sVideoID = 18;
	var
		mylog: DialogPtr;
		item, ignore: integer;
		saveScale: integer;
		saveBuiltin, sVideo: boolean;
		wasDigitizing, WindowClosed, vdigError: boolean;
		saveStandard: VideoDigitizerStandard;
		saveMode: VideoDigitizerMode;
		
	procedure SetCaptureModeButtons;
	begin
		SetDlogItem(mylog, grayID, ord(DigitizerMode = digitizeGrayscale));
		SetDlogItem(mylog, color8ID, ord(DigitizerMode = digitizeColor));
		SetDlogItem(mylog, color24ID, ord(DigitizerMode = digitizeRGB));
	end;

	procedure SetSizeButtons;
	begin
		SetDlogItem(mylog, fullID, ord(fgScale = 1));
		SetDlogItem(mylog, oneHalfID, ord(fgScale = 2));
		SetDlogItem(mylog, oneQuarterID, ord(fgScale = 4));
	end;

	procedure SetStandardButtons;
	begin
		SetDlogItem(mylog, ntscID, ord((DigitizerStandard = defaultStd) or (DigitizerStandard = NTSCStd)));
		SetDlogItem(mylog, palID, ord(DigitizerStandard = palStd));
		SetDlogItem(mylog, secamID, ord(DigitizerStandard = secamStd));
	end;
	
begin
	saveScale := fgScale;
	saveBuiltIn := UseBuiltinDigitizer;
	saveMode := DigitizerMode;
	saveStandard := DigitizerStandard;
	sVideo := VideoChannel = 1;
	InitCursor;
	mylog := GetNewDialog(320, nil, pointer(-1));
	SetCaptureModeButtons;
	SetSizeButtons;
	SetStandardButtons;
	SetDlogItem(mylog, builtinID, ord(UseBuiltinDigitizer));
	SetDlogItem(mylog, sVideoID, ord(sVideo));
	repeat
		ModalDialog(nil, item);
		if item = grayID then begin
			DigitizerMode := digitizeGrayscale;
			SetCaptureModeButtons;
		end;
		if item = color8ID then begin
			DigitizerMode := digitizeColor;
			SetCaptureModeButtons;
		end;
		if item = color24ID then begin
			DigitizerMode := digitizeRGB;
			SetCaptureModeButtons;
		end;
		if item = fullID then begin
			fgScale := 1;
			SetSizeButtons;
		end;
		if item = oneHalfID then begin
			fgScale := 2;
			SetSizeButtons;
		end;
		if item = oneQuarterID then begin
			fgScale := 4;
			SetSizeButtons;
		end;
		if item = ntscID then begin
			DigitizerStandard := ntscStd;
			SetStandardButtons;
		end;
		if item = palID then begin
			DigitizerStandard := palStd;
			SetStandardButtons;
		end;
		if item = secamID then begin
			DigitizerStandard := secamStd;
			SetStandardButtons;
		end;
		if item = builtinID then begin
			UseBuiltinDigitizer := not UseBuiltinDigitizer;
			SetDlogItem(mylog, builtinID, ord(UseBuiltinDigitizer));
		end;
		if item = sVideoID then begin
			sVideo := not sVideo;
			SetDlogItem(mylog, sVideoID, ord(sVideo));
		end;
	until (item = ok) or (item = cancel);
	DisposeDialog(mylog);
	if item = cancel then begin
		fgScale := saveScale;
		UseBuiltinDigitizer := saveBuiltIn;
		DigitizerMode := saveMode;
		DigitizerStandard := saveStandard;
		exit(DoVideoSettingsDialog);
	end;
	if sVideo then
		VideoChannel := 1
	else
		VideoChannel := 0;
	wasDigitizing := digitizing;
	StopDigitizing;
	WindowClosed := false;
	CloseVdig;
	if (fgScale <> saveScale) or (UseBuiltinDigitizer <> saveBuiltIn) or (DigitizerStandard <> saveStandard) then begin
		SelectCameraWindow;
		with info^ do if PictureType = FrameGrabberType then begin
			changes := false;
			ignore := CloseAWindow(wptr);
			WindowClosed := true;
		end;
	end;
	if FrameGrabber = NoFrameGrabber then
		LookForVDig(vdigError);
	if wasDigitizing or WindowClosed then
		StartDigitizing;
end;


procedure SetOffset (var offset, gain: integer);
begin
	if offset < 0 then
		offset := 0;
	if offset > 255 then
		offset := 255;
	if offset > gain then
		offset := gain;
	DacLow := offset;
	DacHigh := DacLow + (255 - gain);
end;


procedure SetGain (var offset, gain: integer);
begin
	if gain < 0 then
		gain := 0;
	if gain > 255 then
		gain := 255;
	if gain < DacLow then
		gain := DacLow;
	DacHigh := DacLow + (255 - gain);
end;


procedure ShowChannel;
begin
	SetDlogItem(VideoControl, FirstChannelID, ord(VideoChannel = 0));
	SetDlogItem(VideoControl, FirstChannelID + 1, ord(VideoChannel = 1));
	SetDlogItem(VideoControl, FirstChannelID + 2, ord(VideoChannel = 2));
	SetDlogItem(VideoControl, FirstChannelID + 3, ord(VideoChannel = 3));
end;


procedure UpdateVideoControl;
begin
	if VideoControl <> nil then
		SetDlogItem(VideoControl, TriggerID, ord(ExternalTrigger));
end;


procedure ShowOffsetAndGain (offset, gain: integer);
	var
		str: str255;
begin
	RealToString(offset, 3, 0, str);
	if str[1] = ' ' then
		str[1] := '0';
	if str[2] = ' ' then
		str[2] := '0';
	SetDString(VideoControl, OffsetID, str);
	RealToString(gain, 3, 0, str);
	if str[1] = ' ' then
		str[1] := '0';
	if str[2] = ' ' then
		str[2] := '0';
	SetDString(VideoControl, GainID, str);
end;


procedure ShowVideoControl;
	var
		gain: integer;
begin
	InitCursor;
	VideoControl := GetNewDialog(130, nil, pointer(-1));
	ShowChannel;
	SetDlogItem(VideoControl, InvertID, ord(InvertVideo));
	SetDlogItem(VideoControl, HighlightID, ord(HighlightSaturatedPixels));
	SetDlogItem(VideoControl, TriggerID, ord(ExternalTrigger));
	SetDlogItem(VideoControl, SyncID, ord(SyncMode = SeparateSync));
	gain := 255 - (DacHigh - DacLow);
	ShowOffsetAndGain(DacLow, gain);
end;


	function NoScion:boolean;
	var
		NotFound:boolean;
	begin
		NotFound:=(FrameGrabber <> ScionLG3) and (FrameGrabber<>ScionAg5) and (FrameGrabber<>ScionVG5f);
		if NotFound then PutError('Programmable offset and gain are only supported on Scion frame grabbers.');
		NoScion:=NotFound;
	end;


	procedure DoVideoControl (item: integer);
	var
		i: integer;
		OutOfRange, WasDigitizing: boolean;
		offset, gain, inc, count: integer;


	procedure SetVideoItem (item, value: integer);
	begin
		if VideoControl <> nil then
			SetDlogItem(VideoControl, item, value);
	end;

begin
	InitCursor;
	gain := 255 - (DacHigh - DacLow);
	if (item >= FirstChannelID) and (item <= (FirstChannelID + 3)) then begin
			VideoChannel := item - FirstChannelID;
			if VideoControl <> nil then
				ShowChannel;
			if digitizing then
				ResetFrameGrabber;
		end;
	if item = InvertID then begin
			InvertVideo := not InvertVideo;
			SetVideoItem(InvertID, ord(InvertVideo));
			if digitizing then
				ResetFrameGrabber;
		end;
	if item = HighlightID then begin
			HighlightSaturatedPixels := not HighlightSaturatedPixels;
			SetVideoItem(HighlightID, ord(HighlightSaturatedPixels));
			if digitizing then begin
					if HighlightSaturatedPixels then
						HighlightPixels
					else
						LoadLUT(info^.ctable);
				end;
		end;
	if item = TriggerID then begin
			ExternalTrigger := not ExternalTrigger;
			case FrameGrabber of
				QuickCapture, ScionLG3, ScionAG5, ScionVG5f:  begin
						WasDigitizing := digitizing;
						StopDigitizing;
						if ExternalTrigger and WasDigitizing then
							StartDigitizing;
					end;
				otherwise
					ExternalTrigger := false;
			end;
			SetVideoItem(TriggerID, ord(ExternalTrigger));
		end;
	if item = SyncID then begin
			if SyncMode <> SeparateSync then
				SyncMode := SeparateSync
			else
				SyncMode := NormalSync;
			case FrameGrabber of
				ScionLG3, ScionAG5, ScionVG5f: 
					if digitizing then
						ResetFrameGrabber;
				QuickCapture:  begin
						PutError('Sync is not under program control on the QuickCapure card.');
						SyncMode := NormalSync;
						AbortMacro;
					end;
				otherwise
					;
			end;
			SetVideoItem(SyncID, ord(SyncMode = SeparateSync));
		end;
	if (item >= OffsetUpID) and (item <= GainDownID) then begin
			if NoScion then exit(DoVideoControl);
			offset := DacLow;
			inc := 1;
			count := 0;
			repeat
				count := count + 1;
				if count > 2 then
					inc := 2;
				if count > 4 then
					inc := 5;
				if count > 8 then
					inc := 10;
				case item of
					OffsetUpID:  begin
							offset := offset + inc;
							SetOffset(offset, gain);
						end;
					OffsetDownID:  begin
							offset := offset - inc;
							SetOffset(offset, gain);
						end;
					GainUpID:  begin
							gain := gain + inc;
							SetGain(offset, gain);
						end;
					GainDownID:  begin
							gain := gain - inc;
							SetGain(offset, gain);
						end;
				end; {case}
				ShowOffsetAndGain(DacLow, gain);
				if Digitizing and (count > 1) then begin
						DacLowReg^ := DacLow;
						DacHighReg^ := DacHigh;
						CaptureAndDisplayFrame;
						if ContinuousHistogram then begin
								ShowContinuousHistogram;
								DrawHistogram
							end
					end
				else
					wait(5);
			until not button;
		end;
	if item = ResetID then begin
			if NoScion then exit(DoVideoControl);
			DacLow := DefaultDacLow;
			DacHigh := DefaultDacHigh;
			gain := 255 - (DacHigh - DacLow);
			ParamText(long2str(DacLow), long2str(gain), '', '');
			ShowOffsetAndGain(DacLow, gain);
		end;
	if FramesToAverage < 2 then
		FramesToAverage := 2;
	if (FrameGrabber = ScionLG3) or (FrameGrabber=ScionAG5) or (FrameGrabber=ScionVG5f) then begin
			DacLowReg^ := DacLow;
			DacHighReg^ := DacHigh;
		end;
end;


procedure ShowVideoDialog;
var
	vdigError: boolean;
begin
	if FrameGrabber = noFrameGrabber then begin
		LookForVDig(vdigError);
		if vdigError then begin
			doVideoSettingsDialog;
			exit(ShowVideoDialog);
		end;
	end;
	if FrameGrabber = QTvdig then
		doVideoSettingsDialog
	else begin
		if VideoControl = nil then
			ShowVideoControl
		else
			SelectWindow(VideoControl);
	end;
end;

end.
