program Image; {Image is a program for the Macintosh II for acquiring, enhancing, analyzing, editing,} {pseudocoloring, printing, and animating grayscale and color images.} {Version 1.37, 19 March 1991} {Developed using Think Pascal 3.0.} {Note: provide at least a 4MB partition for Think Pascal when using MultiFinder.} {Author :} {Wayne Rasband} {National Institutes of Health} {Internet: wayne@helix.nih.gov} {Phone: 301-496-4957} uses QuickDraw, Palettes, PrintTraps, globals, Utilities, Initialization, File1, File2, Analysis, Graphics, edit, Functions, Camera, User, Macros1; {$I-} {PROCEDURE MacsBug; inline $a9ff;} procedure InvertPalette; var TempRed, TempGreen, TempBlue: ColorArrayX; i, LastColor: integer; TempTable: MyCSpecArray; begin with info^ do begin if LutMode = PseudoColor32 then begin TempRed := RedX; TempGreen := GreenX; TempBlue := BlueX; LastColor := ncolors - 1; for i := 0 to LastColor do begin RedX[i] := TempRed[LastColor - i]; GreenX[i] := TempGreen[LastColor - i]; BlueX[i] := TempBlue[LastColor - i]; end; UpdateColors; end else begin TempTable := cTable; for i := 1 to 254 do cTable[i] := TempTable[255 - i]; LoadLUT(cTable); end; end; {with} end; procedure UpdateOptionsMenu; var CheckIt: boolean; i: integer; begin case info^.LUTMode of GrayScale, CustomGrayscale: CheckOnOffItem(OptionsMenuH, GrayscaleItem, GrayscaleItem, RainbowItem); PseudoColor32: CheckOnOffItem(OptionsMenuH, PseudoColorItem, GrayscaleItem, RainbowItem); AppleDefault: CheckOnOffItem(OptionsMenuH, AppleColorsItem, GrayscaleItem, RainbowItem); Spectrum: CheckOnOffItem(OptionsMenuH, RainbowItem, GrayscaleItem, RainbowItem); Custom: for i := GrayscaleItem to RainbowItem do CheckItem(OptionsMenuH, i, false); end; SetMenuItem(OptionsMenuH, ScaleToFitItem, info <> NoInfo); CheckIt := Info^.ScaleToFitWindow; CheckItem(OptionsMenuH, ScaleToFitItem, CheckIt); CheckItem(OptionsMenuH, ThresholdItem, Thresholding); CheckItem(OptionsMenuH, SliceItem, DensitySlicing); SetMenuItem(OptionsMenuH, PropagateItem, nPics > 1); end; procedure UpdateEnhanceMenu; var ShowItems: boolean; i: integer; begin ShowItems := Info <> NoInfo; for i := SmoothItem to ConvolveItem do SetMenuItem(EnhanceMenuH, i, ShowItems); with info^ do if (LutMode = GrayScale) or (LutMode = CustomGrayscale) or DensitySlicing then SetItem(EnhanceMenuH, ApplyItem, 'Apply LUT') else SetItem(EnhanceMenuH, ApplyItem, 'Convert to Grayscale'); for i := BinaryItem to ChangeItem do SetMenuItem(EnhanceMenuH, i, ShowItems); end; procedure UpdateSpecialMenu; var ShowItems: boolean; i: integer; begin ShowItems := Info <> NoInfo; SetMenuItem(SpecialMenuH, SaveBlankFieldItem, ShowItems); SetMenuItem(SpecialMenuH, AnimateItem, ShowItems); SetMenuItem(SpecialMenuH, PhotoModeItem, ShowItems); with info^ do SetMenuItem(SpecialMenuH, SortItem, (LutMode = custom) or (LutMode = AppleDefault) or (LutMode = spectrum)); end; procedure SetNumberOfColors; var TempNColors: integer; begin TempNColors := GetInt('Number Of Colors(1..32):', info^.ncolors); if (TempNColors <= 32) and (TempNColors > 0) then begin info^.nColors := TempNColors; CheckColorWidth; UpdateColors; end else if TempNColors <> -MaxInt then beep; end; procedure SetNumberOfExtraColors; var TempNColors: integer; begin TempNColors := GetInt('Number of Extra Colors(0..6):', nExtraColors); if (TempNColors <= 6) and (TempNColors >= 0) then begin nExtraColors := TempNColors; RedrawLUTWindow; SelectWindow(LUTWindow); if info <> NoInfo then SelectWindow(info^.wptr); end else if TempNColors <> -MaxInt then beep; end; function AboutFilter (d: DialogPtr; var event: EventRecord; var ItemHit: integer): boolean; { simple filter proc for about box -- must be at top level! % } begin if (event.what in [MouseDown, KeyDown, AutoKey]) then begin AboutFilter := true; ItemHit := OK; end else begin AboutFilter := false; ItemHit := 0; end; end; procedure AboutUProc (d: DialogPtr; item: integer); { About box user proc -- must be at top level!} var s: str255; saveport: grafptr; VersInfo: str255; begin getport(saveport); setport(d); if (item = MemItem) then begin NumToString(FreeMem div 1024, s); s := concat(s, 'K free'); DrawSItem(MemItem, Geneva, 9, d, s); end else if (item = VersItem) then begin RealToString(version / 100.0, 4, 2, VersInfo); VersInfo := concat('Version ', VersInfo); DrawSItem(VersItem, Geneva, 9, d, VersInfo); end; setport(saveport); end; procedure DoAbout; {About Box by David Powell} var i: integer; d: dialogptr; midscreen: point; r: rect; h: handle; itype: integer; begin d := getnewdialog(AboutID, nil, pointer(-1)); if (d <> nil) then begin SetPort(d); GetDItem(d, VersItem, itype, h, r); SetDItem(d, VersItem, itype, @AboutUProc, r); GetDItem(d, MemItem, itype, h, r); SetDItem(d, MemItem, itype, @AboutUProc, r); ShowWindow(d); repeat ModalDialog(@aboutfilter, i); until (i = OK); DisposDialog(d); FlushEvents(EveryEvent, 0); end; end; procedure DoPreferences; const WidthID = 4; HeightID = 6; FramesID = 8; InvertID = 9; BufferSizeID = 10; MaxScionWidthID = 14; ScaleArithmeticID = 15; BlackID = 16; InvertYID = 17; ReversingID = 18; LW6ID = 19; var mylog: DialogPtr; item, i: integer; SaveInvert, SaveScale, SaveReversing, SaveLW6: boolean; SaveUseBlack, SaveInvertY: boolean; SaveWidth, SaveHeight, SaveFrames, SaveMaxWidth: integer; SaveBufferSize: LongInt; begin InitCursor; SaveWidth := NewPicWidth; SaveHeight := NewPicHeight; SaveFrames := FramesToAverage; SaveInvert := InvertVideo; SaveBufferSize := BufferSize; SaveMaxWidth := MaxScionWidth; SaveScale := ScaleArithmetic; SaveInvertY := InvertYCoordinates; SaveReversing := ShowReversingMovies; SaveLW6 := UsingLaserWriter6; mylog := GetNewDialog(6000, nil, pointer(-1)); SetDNum(MyLog, WidthID, NewPicWidth); SetDNum(MyLog, HeightID, NewPicHeight); SetDNum(MyLog, FramesID, FramesToAverage); SetDNum(MyLog, BufferSizeID, BufferSize div 1024); SetDNum(MyLog, MaxScionWidthID, MaxScionWidth); SetDialogItem(mylog, InvertID, ord(InvertVideo)); SetDialogItem(mylog, ScaleArithmeticID, ord(ScaleArithmetic)); SetDialogItem(mylog, InvertYID, ord(InvertYCoordinates)); SetDialogItem(mylog, ReversingID, ord(ShowReversingMovies)); SetDialogItem(mylog, LW6ID, ord(UsingLaserWriter6)); UseZeroForBlack := InvertingCalibrationFunction; SaveUseBlack := UseZeroForBlack; if UseZeroForBlack then SetDialogItem(mylog, BlackID, 1); OutlineButton(MyLog, ok, 16); repeat ModalDialog(nil, item); if item = WidthID then begin NewPicWidth := GetDNum(MyLog, WidthID); if (NewPicWidth < 0) or (NewPicWidth > MaxPicSize) then begin NewPicWidth := SaveWidth; SetDNum(MyLog, WidthID, NewPicWidth); end; end; if item = HeightID then begin NewPicHeight := GetDNum(MyLog, HeightID); if (NewPicHeight < 0) or (NewPicHeight > MaxPicSize) then begin NewPicHeight := SaveHeight; SetDNum(MyLog, HeightID, NewPicHeight); end; end; if item = FramesID then begin FramesToAverage := GetDNum(MyLog, FramesID); if (FramesToAverage < 0) or (FramesToAverage > 128) then begin FramesToAverage := SaveFrames; SetDNum(MyLog, FramesID, FramesToAverage); end; end; if item = InvertID then begin StopDigitizing; InvertVideo := not InvertVideo; SetDialogItem(mylog, InvertID, ord(InvertVideo)); end; if item = BufferSizeID then begin BufferSize := GetDNum(MyLog, BufferSizeID) * 1024; if BufferSize < 1 then begin beep; BufferSize := 1; SetDNum(MyLog, BufferSizeID, BufferSize); end; end; if item = MaxScionWidthID then begin MaxScionWidth := BitAnd(GetDNum(MyLog, MaxScionWidthID), $fffc); if (MaxScionWidth < 0) or (MaxScionWidth > 640) then begin beep; MaxScionWidth := SaveMaxWidth; SetDNum(MyLog, MaxScionWidthID, MaxScionWidth); end; end; if item = ScaleArithmeticID then begin ScaleArithmetic := not ScaleArithmetic; SetDialogItem(mylog, ScaleArithmeticID, ord(ScaleArithmetic)); end; if item = BlackID then begin UseZeroForBlack := not UseZeroForBlack; SetDialogItem(mylog, BlackID, ord(UseZeroForBlack)); end; if item = InvertYID then begin InvertYCoordinates := not InvertYCoordinates; SetDialogItem(mylog, InvertYID, ord(InvertYCoordinates)); end; if item = ReversingID then begin ShowReversingMovies := not ShowReversingMovies; SetDialogItem(mylog, ReversingID, ord(ShowReversingMovies)); end; if item = LW6ID then begin UsingLaserWriter6 := not UsingLaserWriter6; SetDialogItem(mylog, LW6ID, ord(UsingLaserWriter6)); end; until (item = ok) or (item = cancel); DisposDialog(mylog); if NewPicWidth < 32 then NewPicWidth := 32; if odd(NewPicWidth) then NewPicWidth := NewPicWidth + 1; if NewPicHeight < 32 then NewPicHeight := 32; if FramesToAverage < 2 then FramesToAverage := 2; if item = cancel then begin NewPicWidth := SaveWidth; NewPicHeight := SaveHeight; FramesToAverage := SaveFrames; BufferSize := SaveBufferSize; MaxScionWidth := SaveMaxWidth; ScaleArithmetic := SaveScale; InvertYCoordinates := SaveInvertY; ShowReversingMovies := SaveReversing; UsingLaserWriter6 := SaveLW6; end else with info^ do begin if UseZeroForBlack and (SaveUseBlack = false) then InvertgrayLevels else if (UseZeroForBlack = false) and SaveUseBlack then begin calibrated := false; DrawLabels('', '', ''); end; end; if BufferSize <> SaveBufferSIze then PutMessage('You must "Record Preferences" and restart before the Undo and Clipboard buffer size change will take effect.'); end; procedure UpdateWindowsMenu; var i, kind: integer; fwptr: WindowPtr; begin for i := NextWindowItem to TileWindowsItem do SetMenuItem(WindowsMenuH, i, nPics > 1); for i := SelectToolsItem to SelectMeasurementsItem do CheckItem(WindowsMenuH, i, false); SetMenuItem(WindowsMenuH, SelectHistogramItem, HistoWindow <> nil); SetMenuItem(WindowsMenuH, SelectPlotItem, PlotWindow <> nil); SetMenuItem(WindowsMenuH, SelectMeasurementsItem, MeasurementsWindow <> nil); for i := 1 to nPics do CheckItem(WindowsMenuH, WindowsMenuItems + i, false); fwptr := FrontWindow; kind := WindowPeek(fwptr)^.WindowKind; if PasteControl = nil then SetItem(WindowsMenuH, PasteControlItem, 'Show Paste Control') else SetItem(WindowsMenuH, PasteControlItem, 'Hide Paste Control'); if kind < 0 then exit(UpdateWindowsMenu); {System Window} case kind of ToolKind: CheckItem(WindowsMenuH, SelectToolsItem, true); GrayMapKind: CheckItem(WindowsMenuH, SelectGrayMapItem, true); LUTKind: CheckItem(WindowsMenuH, SelectLutItem, true); ResultsKind: CheckItem(WindowsMenuH, SelectResultsItem, true); HistoKind: CheckItem(WindowsMenuH, SelectHistogramItem, true); ProfilePlotKind, CalibrationPLotKind: CheckItem(WindowsMenuH, SelectPlotItem, true); MeasurementsKind: CheckItem(WindowsMenuH, SelectMeasurementsItem, true); PicKind: CheckItem(WindowsMenuH, WindowsMenuItems + info^.PicNum, true); otherwise end; end; procedure CloseAll; FORWARD; procedure DoMenuEvent (MenuChoice: LongInt); var MenuID, MenuItem, i, ignore: integer; name, str: str255; dna, RefNum: integer; ItemName: str255; FontName: str255; ok, isSelection: boolean; NewStyle: StyleItem; t: FateTable; {Only needed for MakeSkeleton} begin MenuID := HiWord(MenuChoice); MenuItem := LoWord(MenuChoice); case MenuID of AppleMenu: begin if MenuItem = 1 then DoAbout else begin GetItem(GetMHandle(AppleMenu), MenuItem, name); ignore := OpenDeskAcc(name) end; end; FileMenu: begin StopDigitizing; isInsertionPoint := false; case MenuItem of NewItem: if (LongInt(NewPicWidth) * NewPicHeight) <= UndoBufSize then ok := NewPicWindow('Untitled', NewPicWidth, NewPicHeight) else PutMessage('Sorry, but new windows can''t be larger than the Undo buffer.'); OpenItem: ok := DoOpen('', 0); ImportItem: ok := ImportFile('', 0); CloseItem: if OptionKeyWasDown then CloseAll else DoClose; {-} SaveItem: if OptionKeyDown then SaveAll else SaveFile; SaveAsItem: if FrontWindow = MeasurementsWindow then Export('', 0) else SaveAs('', 0); ExportItem: Export('', 0); SaveScreenItem: SaveScreen; RecordPreferencesItem: SaveSettings; {-} RevertItem: RevertToSaved; DuplicateItem: ok := Duplicate('', false); GetInfoItem: GetInfo; {-} SetHalftoneItem: SetHalftone; PageSetupItem: DoPageSetup; PrintItem: Print(true); {-} QuitItem: finished := true; end; end; EditMenu: begin StopDigitizing; GetItem(GetMHandle(EditMenu), MenuItem, ItemName); if not SystemEdit(MenuItem - 1) then case MenuItem of UndoItem: DoUndo; {-} CutItem: DoCut; CopyItem: DoCopy; PasteItem: begin StopDigitizing; DoPaste end; ClearItem: DoClear; {-} FillItem, InvertItem, DrawBoundaryItem: SetupOperation(MenuItem); DrawScaleItem: DrawScale; {-} SelectAllItem: with info^ do if RoiShowing and EqualRect(RoiRect, PicRect) then KillRoi else begin StopDigitizing; SelectAll(true) end; ScaleAndRotateItem: ScaleAndRotate; {-} RotateLeftItem: Rotate(RotateLeft); RotateRightItem: Rotate(RotateRight); FlipVerticalItem: FlipOrRotate(FlipVertical); FlipHorizontalItem: FlipOrRotate(FlipHorizontal); {-} UnzoomItem: Unzoom; ShowClipboardItem: ShowClipboard; end; end; OptionsMenu: begin case MenuItem of InvertPaletteItem: InvertPalette; SetNumberItem: SetNumberOfColors; SetExtraColorsItem: SetNumberOfExtraColors; {-} GrayscaleItem: ResetGrayMap; PseudoColorItem: UpdateColors; AppleColorsItem: ok := LoadCLUTResource(AppleDefaultCLUT); RainbowItem: Load256ColorCLUT; {-} PreferencesItem: DoPreferences; PlotOptionsItem: DoProfilePlotOptions; ScaleToFitItem: ScaleToFit; ThresholdItem: begin if DensitySlicing then DisableDensitySlice; if Thresholding then ResetGrayMap else EnableThresholding(128); end; SliceItem: if DensitySlicing then DisableDensitySlice else begin if thresholding then ResetGrayMap; EnableDensitySlice; end; end; end; PropagateMenu: DoPropagate(MenuItem); EnhanceMenu: begin SetupUndo; case MenuItem of SmoothItem: if OptionKeyDown then Filter(UnweightedAvg, 0, t) else Filter(WeightedAvg, 0, t); SharpenItem: Filter(fsharpen, 0, t); ShadowItem: Filter(fshadow, 0, t); EdgeDetectItem: Filter(EdgeDetect, 0, t); ReduceNoiseItem: Filter(ReduceNoise, 0, t); DitherItem: Filter(Dither, 0, t); ConvolveItem: Convolve('', 0); {-} ApplyItem: ApplyLookupTable; EnhanceItem: EnhanceContrast; EqualizeItem: EqualizeHistogram; ChangeItem: ChangeValues(ForegroundIndex, ForegroundIndex, BackgroundIndex); end; end; BinaryMenu: case MenuItem of MakeBinaryItem: MakeBinary; ErosionItem: DoErosion; DilationItem: DoDilation; OpeningItem: DoOpening; ClosingItem: DoClosing; IterationsItem: SetIterations; OutlineItem: filter(OutlineFilter, 0, t); SkeletonizeItem: MakeSkeleton; end; ArithmeticMenu: DoArithmetic(MenuItem, 0); AnalyzeMenu: begin SetupUndo; case MenuItem of MeasureItem: Measure; AnalyzeItem: AnalyzeParticles; ShowItem: ShowMeasurements; OptionsItem: DoMeasurementOptions; HistogramItem: DoHistogram; PlotItem: ColumnAveragePlot; Plot3DItem: Do3DPlot; {-} SetScaleItem: SetScale; CalibrateItem: Calibrate; RedoItem: RedoMeasurement; DeleteItem: DeleteMeasurement; ResetItem: ResetCounter; RestoreItem: begin StopDigitizing; RestoreRoi; end; MarkItem: MarkSelection(mCount); end; end; SpecialMenu: begin if MenuItem >= FirstMacroItem then RunMacro(MenuItem - FirstMacroItem + 1); case MenuItem of StartItem: StartDigitizing; AverageItem: AverageFrames; SaveBlankFieldItem: SaveBlankField; SetVideoItem: SetVideoChannel; {-} MakeMovieItem: MakeMovie; AnimateItem: Animate; {-} PhotoModeItem: PhotoMode; {-} LoadMacrosItem: if OpenTextFile(name, RefNum) then LoadMacros(name, RefNum); end; end; SortPaletteMenu: SortPalette(MenuItem); TextMenu: begin case MenuItem of LeftItem: TextJust := teJustLeft; CenterItem: TextJust := teJustCenter; RightItem: TextJust := teJustRight; NoBackgroundItem: TextBack := NoBack; WithBackgroundItem: TextBack := WithBack; end; DisplayText(true); UpdateTextMenu; end; FontMenu: begin GetItem(FontMenuH, MenuItem, FontName); GetFNum(FontName, CurrentFontID); UpdateTextMenu; DisplayText(true); end; SizeMenu: begin case MenuItem of 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12: CurrentSize := GetFontSize(MenuItem); end; DisplayText(true); if IsInsertionPoint then UpdatePicWindow; UpdateTextMenu; end; StyleMenu: begin case MenuItem of 1: CurrentStyle := []; 2, 3, 4, 5, 6: begin case MenuItem of TxBold: NewStyle := Bold; TxItalic: NewStyle := Italic; TxUnderLine: NewStyle := Underline; TxOutLine: NewStyle := Outline; TxShadow: NewStyle := Shadow; end; if NewStyle in CurrentStyle then CurrentStyle := CurrentStyle - [NewStyle] else CurrentStyle := CurrentStyle + [NewStyle]; end; end; {case} DisplayText(true); UpdateTextMenu; end; WindowsMenu: begin case MenuItem of NextWindowItem: ShowNextWindow; StackWindowsItem: StackWindows; TileWindowsItem: TileWindows; PasteControlItem: if PasteControl = nil then ShowPasteControl else ignore := CloseAWindow(PasteControl); {-} SelectToolsItem: SelectWindow(ToolWindow); SelectGrayMapItem: SelectWindow(GrayMapWindow); SelectLutItem: SelectWindow(LUTWindow); SelectResultsItem: SelectWindow(ResultsWindow); SelectHistogramItem: if HistoWindow <> nil then SelectWindow(HistoWindow); SelectPlotItem: if PlotWindow <> nil then SelectWindow(PlotWindow); SelectMeasurementsItem: if MeasurementsWindow <> nil then SelectWindow(MeasurementsWindow); {-} otherwise SelectWindow(PicWindow[MenuItem - WindowsMenuItems]); end; end; UserMenu: DoUserMenuEvent(MenuItem); otherwise end; HiliteMenu(0); RoiUpdateTime := 0; end; procedure DoFreehand (var nVertices: integer; ff: integer; var x, y: xyArray); var finish: point; event: EventRecord; wright, wbottom: integer; b: boolean; begin SetPort(info^.wptr); PenPat(pat[PatIndex]); with info^.wptr^.PortRect do begin wright := right; wbottom := bottom; end; while Button do begin GetMouse(finish); with finish do begin if h < 0 then h := 0; if v < 0 then v := 0; if h > wright then h := wright; if v > wbottom then v := wbottom; end; if nvertices < MaxPolyVertices then nvertices := nvertices + 1 else beep; with finish do begin LineTo(h - ff, v - ff); x[nvertices] := h; y[nvertices] := v; end; b := WaitNextEvent(mUpMask, Event, 4, nil); {Waits 4 seconds} end; {while Button} end; procedure DoPolygon (var nVertices: integer; ff: integer; var x, y: xyArray); var start, Finish, OldFinish: point; finished, DoubleClick, RulerDone: boolean; ticks, MouseUpTime, LastMouseUpTime: LongInt; wright, wbottom: integer; StartRect: rect; Event: EventRecord; begin DrawLabels('DX:', 'DY:', 'Length:'); SetPort(info^.wptr); PenMode(PatXor); with Start do begin h := x[1]; v := y[1]; end; if CurrentTool = PolygonTool then begin Pt2Rect(Start, Start, StartRect); InsetRect(StartRect, -4 * (ff + 1), -4 * (ff + 1)); FrameRect(StartRect); end else SetRect(StartRect, 0, 0, 0, 0); finish := start; finished := false; with info^.wptr^.PortRect do begin wright := right; wbottom := bottom; end; MouseUpTime := 0; RulerDone := false; repeat Show3RealValues(0, 0, 0.0); repeat OldFinish := finish; GetMouse(finish); with finish do begin if h < 0 then begin h := 0; RulerDone := CurrentTool = ruler end; if v < 0 then begin v := 0; RulerDone := CurrentTool = ruler end; if h > wright then begin h := wright; RulerDone := CurrentTool = ruler end; if v > wbottom then begin v := wbottom; RulerDone := CurrentTool = ruler end; end; if not EqualPt(finish, OldFinish) then begin ticks := TickCount; repeat until TickCount <> ticks; MoveTo(start.h - ff, start.v - ff); LineTo(OldFinish.h - ff, OldFinish.v - ff); MoveTo(start.h - ff, start.v - ff); LineTo(finish.h - ff, finish.v - ff); Show3RealValues(abs(finish.h - start.h), abs(finish.v - start.v), sqrt(sqr(LongInt(finish.h - start.h)) + sqr(LongInt(finish.v - start.v)))); end; until WaitNextEvent(mUpMask, Event, 0, nil); LastMouseUpTime := MouseUpTime; MouseUpTime := TickCount; DoubleClick := ((MouseUpTime - LastMouseUpTime) < GetDblTime) and EqualPt(start, finish); if nvertices < MaxPolyVertices then nvertices := nvertices + 1 else beep; x[nvertices] := finish.h; y[nvertices] := finish.v; start := finish; Finished := (PtInRect(finish, StartRect) or DoubleClick or RulerDone) and (nvertices > 2); until finished; repeat until not WaitNextEvent(EveryEvent, Event, 0, nil); {FlushEvent doesn't work under A/UX!} end; procedure MakePolygon (event: EventRecord); var Start, pt, spt: point; nvertices, i, ff, imag: integer; x, y: xyArray; TempRgn: RgnHandle; begin if SelectionMode <> NewSelection then TempRgn := NewRgn; start := event.where; with Info^ do begin SetPort(wptr); imag := trunc(magnification + 0.5); ff := imag div 2; if ff < 0 then ff := 0; PenNormal; PenSize(imag, imag); x[1] := Start.h; y[1] := Start.v; nvertices := 1; end; MoveTo(start.h, start.v); if CurrentTool = FreehandTool then begin DoFreehand(nVertices, ff, x, y); with Start do LineTo(h - ff, v - ff); end else DoPolygon(nVertices, ff, x, y); if nvertices > 2 then with Info^ do begin PenNormal; OpenRgn; spt.h := x[1]; spt.v := y[1]; ScreenToOffscreen(spt); MoveTo(spt.h, spt.v); for i := 2 to nvertices do begin pt.h := x[i]; pt.v := y[i]; ScreenToOffscreen(pt); LineTo(pt.h, pt.v); end; LineTo(spt.h, spt.v); case SelectionMode of NewSelection: CloseRgn(roiRgn); AddSelection: begin CloseRgn(TempRgn); if RgnNotTooBig(roiRgn, TempRgn) then UnionRgn(roiRgn, TempRgn, roiRgn); end; SubSelection: begin CloseRgn(TempRgn); if RgnNotTooBig(roiRgn, TempRgn) then DiffRgn(roiRgn, TempRgn, roiRgn); end; end; RoiShowing := true; roiType := RgnRoi; RoiRect := roiRgn^^.rgnBBox; UpdatePicWindow; if (LengthM in measurements) or (nLengths > 0) then ComputeLength(nvertices, x, y, true) else results.PixelLength := 0.0; end else with info^ do begin RoiShowing := false; RoiType := NoRoi; UpdatePicWindow; end; if SelectionMode <> NewSelection then DisposeRgn(TempRgn); WhatToUndo := NothingToUndo; measuring := false; end; procedure FindCurveLength (event: EventRecord); var Start, p: point; nvertices, ff, i, imag: integer; x, y: xyArray; begin start := event.where; x[1] := start.h; y[1] := start.v; nvertices := 1; with info^ do begin SetPort(wptr); PenNormal; imag := trunc(magnification + 0.5); PenSize(imag, imag); ff := imag div 2 end; if ff < 0 then ff := 0; MoveTo(start.h, start.v); if ControlKeyDown then DoFreehand(nVertices, ff, x, y) else DoPolygon(nVertices, ff, x, y); SetPort(GrafPtr(info^.osPort)); PenNormal; PenSize(LineWidth, LineWidth); p.h := x[1]; p.v := y[1]; ScreenToOffscreen(p); MoveTo(p.h, p.v); for i := 2 to nvertices do begin p.h := x[i] - ff; p.v := y[i] - ff; ScreenToOffscreen(p); LineTo(p.h, p.v); end; UpdatePicWindow; ComputeLength(nvertices, x, y, false); nLengths := nLengths + 1; IncrementCounter; ClearResults(mCount); with info^, results do if SpatialScale <> 0.0 then plength^[mCount] := PixelLength / SpatialScale else plength^[mCount] := PixelLength; ResultsMessage := ''; ShowResults; AppendResults; if nLengths = 1 then UpdateList; measuring := true; end; procedure DoMouseDownInWindow (event: EventRecord; WhichWindow: WindowPtr); {Handles mouse down events in the content region of image windows.} var r: rect; str: str255; hloc, vloc: integer; tool: ToolType; begin if (WindowPeek(WhichWindow)^.WindowKind <> PicKind) then exit(DoMouseDownInWindow); SetPort(info^.wptr); if Digitizing then if (CurrentTool <> MagnifyingGlass) and (CurrentTool <> Grabber) then StopDigitizing; GlobalToLocal(event.where); IsInsertionPoint := false; with info^ do if RoiShowing then if EqualRect(RoiRect, PicRect) and (SelectionMode = NewSelection) then {if Select All} if not (OpPending and (CurrentOp = PasteOp)) then begin KillRoi; MouseState := NotInRoi; exit(DoMouseDownInWindow); end; if MouseState <> NotInRoi then exit(DoMouseDownInWindow); WhatToUndo := UndoEdit; if (SelectionMode = NewSelection) and not ((CurrentTool = MagnifyingGlass) or (CurrentTool = Grabber)) then KillRoi; SetupUndo; if SpaceBarDown and (CurrentTool <> TextTool) then tool := grabber else tool := CurrentTool; case tool of SelectionTool: DoObject(SelectionRect, event); PolygonTool, FreehandTool: MakePolygon(event); LineTool: PutMessage('The line selector tool is under construction.'); OvalSelectionTool: DoObject(SelectionOval, event); MagnifyingGlass: Zoom(event); Grabber: Scroll(event); Pencil, Brush, Eraser: DoBrush(event); AirBrushTool: DoAirBrush; ruler: if OptionKeyDown or ControlKeyDown then FindCurveLength(event) else DoObject(LengthObj, event); PaintBucket: DoFill(event); TextTool: DoText(event.where); PlotTool: DoObject(PlotLine, event); PickerTool: if BitAnd(Event.modifiers, OptionKey) = OptionKey then GetBackgroundColor(event) else GetForegroundColor(event); CrossHairTool: DoPoints(event); AngleTool: FindAngle(event); Wand: begin if Digitizing then StopDigitizing; AutoOutline(event.where); end; otherwise beep; end; end; procedure DoPopupMenusInTools; var MenuItem: integer; ticks, PopUpResult: LongInt; MenuLoc: point; begin BeginUpdate(ToolWindow); DrawTools; EndUpdate(ToolWindow); ticks := TickCount; repeat until (not button) or (TickCount > ticks + 20); if button and (TickCount > (ticks + 20)) then with ToolRect[CurrentTool], MenuLoc do begin MenuLoc.h := left; MenuLoc.v := top; LocalToGlobal(MenuLoc); PopUpResult := PopupMenuSelect(LineToolMenuH, v, h, 1); MenuItem := LoWord(PopUpResult); end; end; procedure DoMouseDownInTools (loc: point); {Handles mouse down events in the tool palette.} var r: rect; OddTool, DoubleClick: boolean; ToolNum, i: integer; begin SetPort(ToolWindow); GlobalToLocal(loc); if loc.v <= StartOfLines then begin PreviousTool := CurrentTool; OddTool := loc.h < tmiddle; ToolNum := (loc.v div tmiddle) * 2; if not OddTool then ToolNum := ToolNum + 1; CurrentTool := ToolType(ToolNum); isSelectionTool := (CurrentTool = SelectionTool) or (CurrentTool = OvalSelectionTool) or (CurrentTool = PolygonTool) or (CurrentTool = FreehandTool) or (CurrentTool = LineTool); DoubleClick := (TickCount - ToolTime) < GetDblTime; ToolTime := TickCount; InvalRect(ToolRect[CurrentTool]); InvalRect(ToolRect[PreviousTool]); IsInsertionPoint := false; if DoubleClick and (CurrentTool = PreviousTool) then case CurrentTool of MagnifyingGlass: Unzoom; SelectionTool: begin StopDigitizing; SelectAll(true); end; AirbrushTool: SetAirbrushSize; Brush: SetBrushSize; ruler: SetScale; PolygonTool: DoMeasurementOptions; FreehandTool: Calibrate; PlotTool: DoProfilePlotOptions; eraser: if info <> NoInfo then begin KillRoi; WhatToUndo := UndoClear; SetupUndo; StopDigitizing; SelectAll(false); DoOperation(eraseOp); end; LutTool, Wand: if DensitySlicing then DisableDensitySlice else begin if info^.deltax <= 1 then {Turn of Gray Map thresholding} ResetGrayMap; EnableDensitySlice; end; PickerTool: if info^.LutMode <> PseudoColor32 then begin {Switch to pseudocolor mode} DisableDensitySlice; UpdateColors; CurrentTool := LutTool; isSelectionTool := false; InvalRect(ToolRect[CurrentTool]); end else ResetGrayMap; otherwise end; {case} if (not isSelectionTool) and (CurrentTool <> MagnifyingGlass) and (CurrentTool <> Grabber) then KillRoi; with info^ do if RoiShowing then if EqualRect(RoiRect, PicRect) and not isSelectionTool then {if Select All} KillRoi; if (CurrentTool = SelectionTool) or (CurrentTool = ruler) or (CurrentTool = CrossHairTool) then begin ResultsMessage := ''; if mCount > 0 then ShowResults; end; StretchMode := false; if Button and (CurrentTool = LineTool) then DoPopUpMenusInTools; end else begin for i := 1 to nLineTypes do begin r := lines[i]; with r do begin left := left - 13; top := top - 2; right := right + 2; bottom := bottom + 2; end; if i = 1 then with r do top := top - 7; if PtInRect(loc, r) then begin with lines[i] do LineWidth := bottom - top; LineIndex := i; end; end; EraseRect(CheckRect); InvalRect(CheckRect); end; end; procedure RotateColors; var vstart, i, j, delta: integer; loc: point; TempTable: MyCSpecArray; begin with info^ do begin SetPort(LUTWindow); GetMouse(loc); vstart := loc.v; repeat GetMouse(loc); delta := vstart - loc.v; for i := 1 to 254 do begin {0 is resevred for white and 255 for black} j := i + delta; if j > 254 then j := j - 254; if j > 254 then j := 254; if j < 1 then j := j + 254; if j < 1 then j := 1; TempTable[i] := cTable[j] end; cTable := TempTable; LoadLUT(cTable); vstart := loc.v; until not button; end; end; procedure ShowSliceValues (tStart, tEnd: integer); var tPort: GrafPtr; begin with info^ do begin GetPort(tPort); SetPort(ResultsWindow); TextSize(9); TextFont(Monaco); TextMode(SrcCopy); MoveTo(xValueLoc, ValuesVStart); if Calibrated then begin DrawReal(cvalue[tStart], 5, 2); DrawString(' ('); DrawReal(tStart, 3, 0); DrawString(')'); end else DrawReal(tStart, 3, 0); DrawString(' '); MoveTo(xValueLoc, ValuesVStart + 10); if Calibrated then begin DrawReal(cvalue[tEnd], 5, 2); DrawString(' ('); DrawReal(tEnd, 3, 0); DrawString(')'); end else DrawReal(tEnd, 3, 0); DrawString(' '); SetPort(tPort); end; end; function GetVLoc: integer; var loc: point; vloc: integer; begin GetMouse(loc); vloc := loc.v; if vloc > 255 then vloc := 255; if vloc <= 0 then vloc := 0; GetVLoc := vloc; end; procedure UpdateThreshold; var level: integer; begin DrawLabels('Thresh:', '', ''); SetPort(LUTWindow); with info^ do repeat level := GetVLoc; if level <= 255 then begin p1x := 255 - level; p2x := 255 - level; UpdateGrayMap; SetGrayScaleLUT; end; Show1Value(255 - p1x, NoValue); until not Button; end; procedure UpdateDensitySlice; var vloc, ThresholdWidth, SaveVLoc, delta: integer; UpdateStart: boolean; CalValue: extended; begin if (CurrentTool = LutTool) or (CurrentTool = Wand) then begin DrawLabels('Lower:', 'Upper:', ''); SetPort(LUTWindow); vloc := GetVLoc; savevloc := vloc; ThresholdWidth := SliceEnd - SliceStart + 1; UpdateStart := vloc <= (SliceStart + ThresholdWidth div 3); if vloc > SliceEnd then SliceEnd := vloc; while button do begin ThresholdWidth := SliceEnd - SliceStart + 1; vloc := GetVloc; delta := vloc - SaveVLoc; SaveVLoc := vloc; if UpdateStart then begin SliceStart := vloc; if SliceStart < 1 then SliceStart := 1 end else begin SliceEnd := SliceEnd + delta; if SliceEnd > 254 then SliceEnd := 254; SliceStart := SliceEnd - ThresholdWidth + 1; if SliceStart < 1 then SliceStart := 1; end; if SliceStart > SliceEnd then SliceStart := SliceEnd; DrawDensitySlice(OptionKeyDown); if UpdateStart then vloc := SliceStart else vloc := SliceEnd; if info^.calibrated then calValue := cvalue[vloc] else calValue := noValue; ShowSliceValues(SliceStart, SliceEnd) end; DrawDensitySlice(false) end else if CurrentTool = PickerTool then EditSliceColor; end; function GetColorFromPalette (DoubleClick: boolean): integer; var mloc, color, i: integer; loc: point; begin SetPort(LUTWindow); GetMouse(loc); if loc.v > 255 then begin color := 0; for i := 1 to nExtraColors + 2 do if PtInRect(loc, ExtraColorsRect[i]) then Color := ExtraColorsEntry[i]; if DoubleClick then EditExtraColors(color); GetColorFromPalette := color; end else GetColorFromPalette := loc.v; end; procedure DoMouseDownInLUT (event: EventRecord); var loc: point; MidPoint, mloc, i, color: integer; DoubleClick: boolean; begin with info^ do begin if CurrentTool = PickerTool then DoubleClick := (TickCount - LutTime) < GetDblTime else DoubleClick := false; LutTime := TickCount; if (CurrentTool <> LutTool) and (CurrentTool <> Wand) then begin color := GetColorFromPalette(DoubleClick); if (CurrentTool = eraser) or OptionKeyDown then SetBackgroundColor(color) else SetForegroundColor(color); if not DoubleClick then exit(DoMouseDownInLUT); end; if Thresholding then begin UpdateThreshold; exit(DoMouseDownInLUT) end; if DensitySlicing then begin UpdateDensitySlice; exit(DoMouseDownInLUT) end; if nColors = 0 then exit(DoMouseDownInLUT); if (LUTMode <> PseudoColor32) and not DoubleClick then begin if DeltaX <> 0 then RotateColors; exit(DoMouseDownInLUT) end; if (CurrentTool = PickerTool) and DoubleClick then begin if LUTMode <> PseudoColor32 then exit(DoMouseDownInLUT); EditColor; exit(DoMouseDownInLUT) end; SetPort(LUTWindow); repeat GetMouse(loc); if loc.v <= 255 then begin mloc := 255 - loc.v; MidPoint := ColorStart + (nColors * ColorWidth) div 2; if mloc < MidPoint then begin ColorStart := mloc; if ColorStart < 0 then ColorStart := 0 end else begin ColorWidth := (mloc - ColorStart) div ncolors; if ColorWidth < 1 then ColorWidth := 1; end; UpdateColors; end; until not Button; end; {with} end; procedure ScaleToFitScreen; var trect: rect; ignore: boolean; begin with info^ do begin MoveWindow(wptr, PicLeftBase, PicTopBase, true); SetRect(trect, 0, 0, ScreenWidth, ScreenHeight); ScaleImageWindow(trect); wrect := trect; SizeWindow(wptr, trect.right, trect.bottom, true); end; end; procedure DoDrag (WhichWindow: WindowPtr; loc: point); var WinRect, DragBounds, trect: rect; kind: integer; begin kind := WindowPeek(WhichWindow)^.WindowKind; if kind = PicKind then begin if info^.PictureType = ScionType then exit(DoDrag); with info^ do begin {Save window location} GetWindowRect(wptr, trect); savehloc := trect.left; savevloc := trect.top; end; PicLeft := PicLeftBase; PicTop := PicTopBase; end; DragBounds := ScreenBits.bounds; DragWindow(WhichWindow, loc, DragBounds); if WhichWindow = ResultsWindow then ShowResults; end; procedure UpdateMenus; begin OptionKeyWasDown := OptionKeyDown; UpdateFileMenu; UpdateEditMenu; UpdateOptionsMenu; UpdateEnhanceMenu; UpdateAnalysisMenu; UpdateSpecialMenu; UpdateWindowsMenu; end; procedure DoMouseDown (event: EventRecord); var WhichWindow: WindowPtr; ThePart, ignore: integer; trect: rect; begin ThePart := FindWindow(event.where, WhichWindow); case ThePart of InDesk: ; InMenuBar: begin UpdateMenus; DoMenuEvent(MenuSelect(event.where)); end; InSysWindow: SystemClick(Event, WhichWindow); InContent: begin RoiUpdateTime := 0; if WhichWindow = ToolWindow then begin DoMouseDownInTools(event.where); exit(DoMouseDown); end; if WhichWindow = GrayMapWindow then begin DoMouseDownInGrayMap; exit(DoMouseDown) end; if WhichWindow = LUTWindow then begin DoMouseDownInLUT(event); exit(DoMouseDown) end; if WhichWindow = PasteControl then begin DoMouseDownInPasteControl(event.where); exit(DoMouseDown) end; if WhichWindow = MeasurementsWindow then begin DoMouseDownInMeasurements(event.where); exit(DoMouseDown) end; if WhichWindow <> FrontWindow then SelectWindow(WhichWindow) else DoMouseDownInWindow(Event, WhichWindow); end; InDrag: DoDrag(WhichWindow, event.where); InGrow: DoGrow(WhichWindow, event); InGoAway: if TrackGoAway(WhichWindow, event.where) then if OptionKeyDown and (WindowPeek(WhichWindow)^.WindowKind = PicKind) then CloseAll else begin StopDigitizing; ignore := CloseAWindow(WhichWindow); end; InZoomIn, InZoomOut: with info^ do case WindowState of NormalWindow: begin if digitizing then exit(DoMouseDown); ScaleToFit; if ScaleToFitWindow then ScaleToFitScreen; end; TiledSmall, TiledSmallScaled: begin if WindowState = TiledSmall then begin ScaleToFitWindow := true; WindowState := TiledBig; end else WindowState := TiledBigScaled; savewrect := wrect; SaveSrcRect := SrcRect; SaveMagnification := magnification; GetWindowRect(wptr, trect); savehloc := trect.left; savevloc := trect.top; ScaleToFitScreen; UpdatePicWindow; end; TiledBig: begin ScaleToFitWindow := false; WindowState := TiledSmall; wrect := savewrect; SrcRect := SaveSrcRect; magnification := SaveMagnification; HideWindow(wptr); SizeWindow(wptr, wrect.right, wrect.bottom, true); MoveWindow(wptr, savehloc, savevloc, true); ShowWindow(wptr); UpdatePicWindow; magnification := 1.0; ShowMagnification; end; TiledBigScaled: begin WindowState := TiledSmallScaled; wrect := savewrect; SrcRect := PicRect; HideWindow(wptr); SizeWindow(wptr, wrect.right, wrect.bottom, true); MoveWindow(wptr, savehloc, savevloc, true); ShowWindow(wptr); UpdatePicWindow; if PicRect.right <> 0 then magnification := wrect.right / PicRect.right; ShowMagnification; end; end; {case WindowState} end; {case thePart} end; procedure NudgeRoi (key: integer); var dh, dv: integer; begin with info^ do begin if not RoiShowing then exit(NudgeRoi); case key of LeftArrow: begin dh := -1; dv := 0 end; RightArrow: begin dh := 1; dv := 0 end; UpArrow: begin dh := 0; dv := -1 end; DownArrow: begin dh := 0; dv := 1 end; end; if OptionKeyDown then begin if RoiType = RectRoi then with RoiRect do begin right := right + dh; if right < left + 2 then right := left + 2; bottom := bottom + dv; if bottom < top + 2 then bottom := top + 2; MakeRegion; end else beep; end else begin OffsetRgn(roiRgn, dh, dv); RoiRect := roiRgn^^.rgnBBox; end; RoiNudged := true; RoiUpdateTime := 0; end; end; procedure DoKeyDown (event: EventRecord); var Ch: char; ich, KeyCode: integer; begin Ch := chr(band(Event.message, CharCodeMask)); ich := ord(ch); KeyCode := bsr(band(Event.message, KeyCodeMask), 8); if BitAnd(Event.modifiers, CmdKey) = CmdKey then begin UpdateMenus; if OptionKeyWasDown then begin case KeyCode of 1: ch := 'S'; 5: ch := 'G'; 8: ch := 'C'; 13: ch := 'W'; 17: ch := 'T'; 35: ch := 'P'; end; end; DoMenuEvent(MenuKey(Ch)); exit(DoKeyDown) end; if (CurrentTool = TextTool) and IsInsertionPoint and (ord(ch) <> FunctionKey) then DrawCharacter(ch) else if ch = BackSpace then DoClear else if (ich >= LeftArrow) and (ich <= DownArrow) then NudgeRoi(ich) else if nMacros > 0 then RunKeyMacro(ch, KeyCode); end; procedure DoActivate (event: EventRecord); var WhichWindow: WindowPtr; Activating, SwitchingWindows, isOK: boolean; I, kind: integer; NewInfo: InfoPtr; begin WhichWindow := WindowPtr(event.message); kind := WindowPeek(WhichWindow)^.WindowKind; Activating := odd(event.modifiers); case kind of PicKind: begin if Activating then begin NewInfo := pointer(WindowPeek(WhichWindow)^.RefCon); SwitchingWindows := NewInfo <> Info; if SwitchingWindows then begin StopDigitizing; SaveRoi; DisableDensitySlice; end; Info := NewInfo; if SwitchingWindows then ActivateWindow else SetPort(info^.wptr); with info^ do begin if (LutMode = GrayScale) or (LutMode = CustomGrayscale) then DrawGrayMap; LoadLUT(cTable); GenerateValues; if not calibrated then DrawLabels('', '', ''); end; {with} end else KillOperation; {Deactivate} end; MeasurementsKind: UpdateMeasurementsWindow; otherwise end; {case} if not activating then begin WhichWindow := FrontWindow; kind := WindowPeek(WhichWindow)^.WindowKind; if kind < 0 then ConvertClipboard; {DA has become active} end; end; procedure DoUpdate (event: EventRecord); var WhichWindow: WindowPtr; SaveInfo: InfoPtr; kind: integer; begin WhichWindow := WindowPtr(event.message); kind := WindowPeek(WhichWindow)^.WindowKind; BeginUpdate(WhichWindow); case kind of Pickind: begin SaveInfo := info; Info := pointer(WindowPeek(WhichWindow)^.RefCon); if not digitizing then begin UpdatePicWindow; DrawMyGrowIcon(info^.wptr); end; info := SaveInfo; end; ToolKind: DrawTools; GrayMapKind: DrawGrayMap; LUTKind: DrawLUT; ResultsKind: begin DrawLabels('', '', ''); if (mCount > 0) or (ResultsMessage <> '') then ShowResults; end; HistoKind: DrawHistogram; ProfilePlotKind, CalibrationPlotKind: UpdatePlotWindow; MeasurementsKind: UpdateMeasurementsWindow; PasteControlKind: DrawPasteControl; end; EndUpdate(WhichWindow); end; procedure DoDiskInsert (event: EventRecord); { Process disk insertion event, check for damaged or uninitialized disks. } var p: point; intjunk: integer; begin if (HiWord(event.message) <> NoErr) then begin DiLoad; SetPt(p, 100, 80); intjunk := DIBadMount(p, event.message); DiUnload; end; end; function HandleEvents: boolean; const mousemovedmessage = $FA; SuspendResumeMessage = 1; ResumeMask = 1; var Event: EventRecord; result: boolean; theDialog: DialogPtr; ItemHit: integer; begin if WaitNextEvent(EveryEvent, Event, 2, nil) then begin case Event.what of KeyDown, AutoKey: DoKeyDown(Event); MouseDown: DoMouseDown(Event); ActivateEvt: DoActivate(Event); DiskEvt: DoDiskInsert(Event); UpdateEvt: DoUpdate(Event); app4Evt: case BSR(event.message, 24) of MouseMovedMessage: ; SuspendResumeMessage: if BAND(event.message, ResumeMask) <> 0 then {Resume event} LoadLUT(info^.ctable) else begin {Suspend event} KillOperation; ConvertClipboard; end; end; otherwise {Do nothing} end; HandleEvents := true end else HandleEvents := false; end; procedure ShowInsertionPoint; var tRect: rect; Loc: point; height, imag: integer; begin if (not isInsertionPoint) or (info = NoInfo) then exit(ShowInsertionPoint); if (TickCount mod (BlinkTime * 2)) < BlinkTime then exit(ShowInsertionPoint); if info = NoInfo then exit(ShowInsertionPoint); Loc := InsertionPoint; OffscreenToScreen(loc); with info^, tRect do begin SetPort(wptr); imag := trunc(magnification + 0.5); height := CurrentSize * imag; height := height - height div 4; left := loc.h; bottom := loc.v - imag + 1; top := bottom - height; right := left + 1; PenNormal; PenSize(imag, imag); PenMode(PatXor); FrameRect(tRect); ticks := TickCount + 3; repeat until TickCount > ticks; FrameRect(tRect); end; end; procedure UndoRoi; var SrcPtr, DstPtr: ptr; offset, ByteCount: LongInt; tTop, tBottom: LongInt; begin with info^ do begin if PixMapSize <> CurrentUndoSize then exit(UndoRoi); with RoiRect do begin tTop := top; tBottom := bottom; if tTop < 0 then tTop := 0; if tTop > PicRect.bottom then tTop := PicRect.bottom; if tBottom < 0 then tBottom := 0; if tBottom > PicRect.bottom then tBottom := PicRect.bottom; end; offset := tTop * BytesPerRow; if offset < 0 then offset := 0; SrcPtr := ptr(ord4(UndoBuf) + offset); DstPtr := ptr(ord4(PicBaseAddr) + offset); ByteCount := (tBottom - tTop) * BytesPerRow; BlockMove(SrcPtr, DstPtr, ByteCount); end; end; procedure DrawROI; var tRect: rect; RoiStretchHandle: rect; psize: integer; StartTicks: LongInt; begin with Info^ do begin StartTicks := TickCount; if OpPending then DoOperation(CurrentOp); SetPort(GrafPtr(Info^.osPort)); PenNormal; if ScaleToFitWindow then if (magnification < 1.0) and (magnification <> 0.0) then begin psize := round(1.0 / magnification + 1.5); PenSize(psize, psize); end; if not ((MouseState = DownInRoi) and OpPending) then if PixMapSize <= UndoBufSize then begin pmForeColor(BlackIndex); pmBackColor(WhiteIndex); if RoiType = RectRoi then begin with RoiRect do begin SetRect(RoiStretchHandle, right - RoiHandleSize, bottom - RoiHandleSize, right, bottom); if ((right - left) > RoiHandleSize) and ((bottom - top) > RoiHandleSize) then PaintRect(RoiStretchHandle); end; end; PatIndex := (PatIndex + 1) mod 8; PenPat(pat[PatIndex]); FrameRgn(roiRgn); pmForeColor(ForegroundIndex); pmBackColor(BackgroundIndex); end; if PixMapSize > UndoBufSize then begin if magnification < 1.0 then PenSize(2, 2); PatIndex := (PatIndex + 1) mod 8; PenPat(pat[PatIndex]); PenMode(PatXor); FrameRgn(roiRgn); if MouseState = DownInRoi then begin UnionRect(RoiRect, OldRoiRect, tRect); UpdateScreen(tRect); end else UpdateScreen(RoiRect); FrameRgn(roiRgn); end else begin tRect := RoiRect; if MouseState = DownInRoi then UnionRect(RoiRect, OldRoiRect, tRect) else if RoiNudged then begin tRect := RoiRect; RoiNudged := false; end; InsetRect(tRect, -2, -2); UpdateScreen(tRect); UndoRoi; {Erase offscreen ROI} end; RoiUpdateTime := TickCount - StartTicks; end; {with} end; procedure MoveRoi (loc, osloc: point); var osdh, osdv: integer; begin with info^ do begin osdh := osloc.h - osMouseDownLoc.h; osdv := osloc.v - osMouseDownLoc.v; if RoiMovementState = Constrained then begin if osdv <> 0 then RoiMovementState := ConstrainedV else if osdh <> 0 then RoiMovementState := ConstrainedH end; if RoiMovementState = ConstrainedH then osdv := 0 else if RoiMovementState = ConstrainedV then osdh := 0; if not OpPending then with RoiRect do begin if (left + osdh < 0) and not StretchMode then osdh := -left; if (top + osdv < 0) and not StretchMode then osdv := -top; if right + osdh > PicRect.right then osdh := PicRect.right - right; if bottom + osdv > PicRect.bottom then osdv := PicRect.bottom - bottom; end; OldRoiRect := RoiRect; if StretchMode then begin measuring := false; DrawLabels('Width:', 'Height:', ''); with RoiRect do begin right := right + osdh; if right < left + 2 then right := left + 2; bottom := bottom + osdv; if bottom < top + 2 then bottom := top + 2; Show3Values(right - left, bottom - top, -1); MakeRegion; end end else begin DrawLabels('X:', 'Y:', 'Value:'); OffsetRgn(roiRgn, osdh, osdv); with RoiRect do Show3Values(left, top, 0); end; RoiRect := roiRgn^^.rgnBBox; MouseDownLoc := loc; osMouseDownLoc := osloc; RoiUpdateTime := 0; {Forces ROI outline to be redrawn} end; {with Info} end; procedure ShowHistogramValues (GrayLevel: LongInt); var hstart, vstart, ivalue: integer; begin hstart := ValuesHStart; vstart := ValuesVStart; SetPort(ResultsWindow); TextSize(9); TextFont(Monaco); TextMode(SrcCopy); MoveTo(xValueLoc, vstart); with info^ do if calibrated then begin if InvertingCalibrationFunction then DrawReal(cvalue[255 - GrayLevel], 8, 2) else DrawReal(cvalue[GrayLevel], 8, 2); DrawString(' ('); DrawLong(GrayLevel); DrawString(' )'); end else DrawLong(GrayLevel); DrawString(' '); MoveTo(yValueLoc, vstart + 10); if InvertingCalibrationFunction then DrawLong(histogram[255 - GrayLevel]) else DrawLong(histogram[GrayLevel]); DrawString(' '); end; procedure SelectCursor; var loc, osloc, gloc: point; where, kind, i, color, x, y, margin: integer; WhichWindow: WindowPtr; MouseInRoi: boolean; fwptr: WindowPtr; CalValue, xscale: extended; RoiStretchHandle: rect; MovingRoi: boolean; xvalue, pvalue: integer; begin if PasteControl <> nil then begin fwptr := FrontWindow; if WindowPeek(fwptr)^.WindowKind <> PasteControlKind then BringToFront(PasteControl); end; SetPort(ScreenPort); GetMouse(gloc); loc := gloc; where := FindWindow(gloc, WhichWindow); if WhichWindow = nil then begin InitCursor; exit(SelectCursor) end; kind := WindowPeek(WhichWindow)^.WindowKind; if kind < 0 then exit(SelectCursor); {System Window} if where <> InContent then begin InitCursor; exit(SelectCursor) end; case kind of PicKind: begin if Info = NoInfo then begin InitCursor; exit(SelectCursor) end; SetPort(info^.wptr); GlobalToLocal(loc); osloc := loc; ScreenToOffscreen(osloc); MovingRoi := false; with info^ do begin SelectionMode := NewSelection; if RoiShowing and isSelectionTool then begin if OptionKeyDown then SelectionMode := SubSelection else if ControlKeyDown then SelectionMode := AddSelection; end; if RoiShowing and (SelectionMode = NewSelection) then MouseInRoi := PtInRgn(osloc, roiRgn) else MouseInRoi := false end; {with} if MouseInRoi or (MouseState = DownInRoi) then begin if MouseState = NotInRoi then MouseState := InRoi; InitCursor; if button then begin if MouseState = InRoi then begin if OpPending and (CurrentOp <> PasteOp) then SetupUndo; MouseState := DownInRoi; MouseDownLoc := loc; osMouseDownLoc := osloc; with info^ do if RoiType = RectRoi then begin if magnification > 1.0 then margin := 0 else margin := 2; with RoiRect do SetRect(RoiStretchHandle, right - RoiHandleSize - margin, bottom - RoiHandleSize - margin, right, bottom); StretchMode := PtInRect(osloc, RoiStretchHandle); end; if ShiftKeyDown then RoiMovementState := Constrained else RoiMovementState := Unconstrained; end; MoveRoi(loc, osloc); MovingRoi := true; end else MouseState := InRoi end else begin MouseState := NotInRoi; if SpaceBarDown and (CurrentTool <> TextTool) then SetCursor(ToolCursor[Grabber]) else if SelectionMode = AddSelection then SetCursor(CrossPlusCursor) else if SelectionMode = SubSelection then SetCursor(CrossMinusCursor) else if (CurrentTool = MagnifyingGlass) and OptionKeyDown then SetCursor(GlassMinusCursor) else SetCursor(ToolCursor[CurrentTool]); end; if not MovingRoi then begin DrawLabels('X:', 'Y:', 'Value:'); with osloc do begin if Digitizing then pvalue := GetQCPixel(h, v) else pvalue := MyGetPixel(h, v); Show3Values(h, v, pvalue); end; end; end; HistoKind: begin DrawLabels('X:', 'Y:', ''); SetCursor(ToolCursor[SelectionTool]); SetPort(HistoWindow); GlobalToLocal(loc); ShowHistogramValues(loc.h); end; ProfilePlotKind, CalibrationPlotKind: begin DrawLabels('X:', 'Y:', ''); SetCursor(ToolCursor[SelectionTool]); SetPort(PlotWindow); GlobalToLocal(loc); xscale := PlotCount / (PlotWidth - PlotRightMargin - PlotLeftMargin); xvalue := trunc((loc.h - PlotLeftMargin) * xscale); if (xvalue >= 0) and (xvalue < PlotWidth) then if kind = CalibrationPlotKind then Show2CalibratedValues(xvalue, xvalue, false) else Show2CalibratedValues(xvalue, PlotData[xvalue], true); end; LUTKind: begin DrawLabels('Value:', '', ''); SetPort(LUTWindow); GlobalToLocal(loc); if (CurrentTool = LutTool) or (CurrentTool = Wand) then begin if loc.v < 256 then SetCursor(LUTCursor) else InitCursor end else SetCursor(PickerCursor); if loc.v < 256 then begin if info^.calibrated then calValue := cvalue[loc.v] else calValue := noValue; Show1Value(loc.v, calValue); end else begin color := 0; for i := 1 to nExtraColors + 2 do if PtInRect(loc, ExtraColorsRect[i]) then Color := ExtraColorsEntry[i]; if info^.calibrated then calValue := cvalue[color] else calValue := noValue; Show1Value(color, calValue); end; end; GrayMapKind: SetCursor(gmCursor); otherwise InitCursor; end; {case} end; procedure CloseAll; var i, j, result: integer; WPeek, NextWPeek: WindowPeek; ignore: boolean; begin InitCursor; WPeek := WindowPeek(FrontWindow); StopDigitizing; while wpeek <> nil do begin NextWPeek := WPeek^.NextWindow; if WPeek^.WindowKind = PicKind then begin Info := pointer(WPeek^.RefCon); result := CloseAWindow(info^.wptr); if not CommandPeriod then for j := 1 to 2 do ignore := HandleEvents; if result = cancel then begin ActivateWindow; finished := false; exit(CloseAll) end; end; wpeek := NextWPeek; end; end; procedure DoStartup; {Process Finder startup information} var message, ndocs, err, i, j: integer; DocInfo: AppFile; DefaultPalette, OpenedOK: boolean; palettename: str255; PaletteFile: boolean; ignore, PrintDocs: boolean; procedure PrintDocument; var i: integer; begin WhatToPrint := PrintImage; Print(false); DoClose; for i := 1 to 10 do ignore := HandleEvents; end; begin for j := 1 to 10 do ignore := HandleEvents; PaletteFile := false; CountAppFiles(message, ndocs); PrintDocs := message = appPrint; if ndocs >= 1 then for i := 1 to ndocs do begin GetAppFiles(i, DocInfo); with DocInfo do begin if ftype = 'ICOL' then begin PaletteFile := true; PaletteName := docinfo.fname; ClrAppFiles(i) end; if fType = 'IPIC' then begin WhatToOpen := OpenImage; OpenedOK := OpenFile(fName, vRefNum); for j := 1 to 10 do ignore := HandleEvents; ClrAppFiles(i); if not OpenedOK then exit(DoStartup); if PrintDocs then PrintDocument; end; if fType = 'TIFF' then begin WhatToOpen := OpenTIFF; OpenedOK := OpenFile(fName, vRefNum); for j := 1 to 10 do ignore := HandleEvents; ClrAppFiles(i); if not OpenedOK then exit(DoStartup); if PrintDocs then PrintDocument; end; if fType = 'PICT' then begin OpenedOK := OpenPICT(fName, vRefNum, false); for j := 1 to 10 do ignore := HandleEvents; ClrAppFiles(i); if not OpenedOK then exit(DoStartup); if PrintDocs then PrintDocument; end; end; {with} end; if PaletteFile then LoadPseudoColorPalette(PaletteName, DocInfo.vRefNum); end; procedure LoadDefaultMacros; {Looks for a text file named "Image Macros" in the same folder as} {Image, and, if found, loads the macros contained in it.} var err: OSErr; LaunchRefNum: integer; FinderInfo: FInfo; begin err := GetVol(nil, LaunchRefNum); err := GetFInfo('Image Macros', LaunchRefNum, FinderInfo); if err = NoErr then begin LoadMacros('Image Macros', LaunchRefNum); UnloadSeg(@LoadMacros); end; end; procedure Shutdown; var AlertID: integer; begin if (UnsavedResults and (mCount > 10)) or (UnsavedResults and (MeasurementsWindow <> nil)) then begin InitCursor; AlertID := alert(500, nil); if AlertID = CancelResetID then begin finished := false; exit(Shutdown) end; end; CloseAll; if finished then ConvertClipboard; end; begin Init; SetupMenus; GetSettings; AllocateBuffers; AllocateArrays; ConvertSystemClipboard; DoStartup; LoadDefaultMacros; UnloadSeg(@Init); InitUser; repeat if not HandleEvents then if info^.RoiShowing and (RoiUpdateTime < 30) then DrawRoi; ShowInsertionPoint; SelectCursor; if Digitizing then begin CaptureAndDisplayQCFrame; if ContinuousHistogram then ShowContinuousHistogram; end; if Finished then Shutdown; until finished; isOK := LoadCLUTResource(AppleDefaultCLUT); RestoreScreen; {Force Finder to redraw color icons} end.