unit Utilities; {Miscellaneous utility routines used by Image program} interface uses QuickDraw, Palettes, Picker, PrintTraps, globals;{SANE} procedure SetDialogItem (TheDialog: DialogPtr; item, value: integer); procedure OutlineButton (theDialog: DialogPtr; itemNo, CornerRad: integer); function GetDNum (TheDialog: DialogPtr; item: integer): LongInt; function GetDString (TheDialog: DialogPtr; item: integer): str255; procedure SetDNum (TheDialog: DialogPtr; item: integer; n: LongInt); procedure GetWindowRect (w: WindowPtr; var wrect: rect); procedure SetDReal (TheDialog: DialogPtr; item: integer; n: extended; fwidth: integer); procedure SetDString (TheDialog: DialogPtr; item: integer; str: str255); procedure DrawSItem (itemnum, fontrqst, sizerqst: integer; d: dialogptr; s: str255); function StringToReal (str: str255): real; function GetDReal (TheDialog: DialogPtr; item: integer): extended; procedure RealToString (Val: extended; width, fwidth: integer; var Str: Str255); procedure DrawReal (Val: extended; width, fwidth: integer); procedure DrawLong (i: LongInt); function GetInt (message: str255; default: integer): integer; function GetReal (message: str255; default: extended): extended; function OptionKeyDown: boolean; function ShiftKeyDown: boolean; function ControlKeyDown: boolean; function CommandPeriod: boolean; function SpaceBarDown: boolean; procedure SysResume; procedure beep; procedure PutMessage (str: str255); procedure UpdateTextMenu; procedure RedrawLUTWindow; procedure Load256ColorCLUT; function LoadCLUTResource (id: integer): boolean; procedure UnprotectLUT; procedure LoadLUT (table: MyCSpecArray); procedure DrawDensitySlice (OptionKey: boolean); procedure SelectLutTool; procedure EnableDensitySlice; procedure DisableDensitySlice; procedure UpdateColors; procedure LoadInputLookupTable (address: ptr); procedure ResetQuickCapture; procedure GetLookupTable (var table: LookupTable); procedure wait (ticks: LongInt); procedure SetGrayScaleLUT; procedure CheckColorWidth; procedure GetDefaultPalette; procedure GetPaletteFromFile (fname: str255; vnum: integer); procedure InitColor (fname: str255; vnum: integer); function GetScrapCount: integer; procedure DisplayText (update: boolean); procedure SetForegroundColor (color: integer); procedure SetBackgroundColor (color: integer); procedure ScreenToOffscreen (var loc: point); procedure OffscreenToScreen (var loc: point); procedure OffScreenToScreenRect (var r: rect); procedure UpdateScreen (MaskRect: rect); function GetColorIndex: integer; procedure RestoreRoi; procedure Undo; procedure CheckOnOffItem (MenuH: MenuHandle; item, fst, lst: Integer); procedure SetMenuItem (menuh: menuhandle; itemnum: integer; on: boolean); function GetFontSize (item: integer): integer; function MyGetPixel (h, v: integer): integer; procedure PutPixel (h, v, value: integer); procedure GetLine (h, v, count: integer; var line: LineType); procedure GetColumn (hstart, vstart, count: integer; var data: LineType); procedure PutColumn (hstart, vstart, count: integer; var data: LineType); procedure PutLine (h, v, count: integer; var line: LineType); procedure Show1Value (rvalue, CalibratedValue: extended); procedure Show2CalibratedValues (x, y: LongInt; ShowUncalibrated: boolean); procedure Show2Values (current, total: LongInt); procedure DrawDimension (x: integer); procedure Show3Values (hloc, vloc, ivalue: LongInt); procedure Show3RealValues (X, Y: LongInt; Z: extended); procedure PutChar (c: char); procedure PutTab; procedure PutString (str: str255); procedure PutReal (n: extended; width, fwidth: integer); procedure PutLong (n: LongInt; FieldWidth: integer); procedure CopyResultsToBuffer (FirstCount, LastCount: integer; Headings: boolean); procedure ShowWatch; procedure UpdatePicWindow; procedure DoOperation (Operation: OpType); procedure SaveRoi; procedure KillRoi; procedure Paste; procedure ShowRoi; procedure SetupUndo; procedure SetupUndoFromClip; function NotRectangular: boolean; function NotInBounds: boolean; function NoSelection: boolean; function NoUndo: boolean; function NewPicWindow (name: str255; width, height: integer): boolean; procedure MakeRegion; procedure SelectAll (visible: boolean); procedure EraseScreen; procedure RestoreScreen; procedure ShowMagnification; procedure Unzoom; function FindMedian (var a: SortArray): integer; procedure DrawBString (str: string); procedure DrawMyGrowIcon (w: WindowPtr); procedure PutMemoryAlert; function GetImageMemory (SaveInfo: infoPtr; var PicBaseHandle: handle; double: boolean): ptr; procedure UpdateAnalysisMenu; procedure ExtendWindowsMenu (fname: str255; size: LongInt; wptr: WindowPtr); procedure MakeNewWindow (name: str255); procedure PutWarning; procedure ScaleToFit; procedure SetupRoiRect; procedure GetForegroundColor (event: EventRecord); procedure GetBackgroundColor (event: EventRecord); procedure GenerateValues; procedure KillOperation; procedure ScaleImageWindow (var trect: rect); procedure InvertGrayLevels; function TooWide: boolean; procedure DrawText (str: str255; loc: point; just: integer); procedure IncrementCounter; procedure ClearResults (i: integer); procedure UpdateFitEllipse; implementation type KeyPtrType = ^KeyMap; procedure MacsBug (str: str255); inline $abff; procedure SetDialogItem;{(TheDialog:DialogPtr; item,value:integer)} var ItemType: integer; ItemBox: rect; ItemHdl: handle; begin GetDItem(TheDialog, item, ItemType, ItemHdl, ItemBox); SetCtlValue(ControlHandle(ItemHdl), value) end; procedure OutlineButton;{(theDialog: DialogPtr; itemNo, CornerRad: integer)} { Draws a border around a button. 16 is the normal} { cornerRad for small buttons } var itemType: Integer; itemBox: Rect; itemHdl: Handle; tempPort: GrafPtr; begin GetPort(tempPort); SetPort(theDialog); GetDItem(theDialog, itemNo, itemType, itemHdl, itemBox); PenSize(3, 3); InSetRect(itemBox, -4, -4); FrameRoundRect(itemBox, cornerRad, cornerRad); PenSize(1, 1); SetPort(tempPort); end; function GetDNum;{(TheDialog:DialogPtr; item:integer):LongInt} var ItemType: integer; ItemBox: rect; ItemHdl: handle; str: str255; n: LongInt; begin GetDItem(TheDialog, item, ItemType, ItemHdl, ItemBox); GetIText(ItemHdl, str); StringToNum(str, n); GetDNum := n; end; function GetDString;{(TheDialog:DialogPtr; item:integer):str255} var ItemType: integer; ItemBox: rect; ItemHdl: handle; str: str255; begin GetDItem(TheDialog, item, ItemType, ItemHdl, ItemBox); GetIText(ItemHdl, str); GetDString := str; end; procedure SetDNum;{(TheDialog:DialogPtr; item:integer; n:LongInt)} var ItemType: integer; ItemBox: rect; ItemHdl: handle; str: str255; begin GetDItem(TheDialog, item, ItemType, ItemHdl, ItemBox); NumToString(n, str); SetIText(ItemHdl, str) end; procedure GetWindowRect;{(w:WindowPtr; VAR wrect:rect)} {Returns global coordinates of specified window.} begin wrect := WindowPeek(w)^.contRgn^^.rgnBBox; end; procedure SetDReal;{(TheDialog:DialogPtr; item:integer; n:extended; fwidth:integer)} var ItemType: integer; ItemBox: rect; ItemHdl: handle; str: str255; begin GetDItem(TheDialog, item, ItemType, ItemHdl, ItemBox); RealToString(n, 1, fwidth, str); SetIText(ItemHdl, str) end; procedure SetDString;{(TheDialog:DialogPtr; item:integer; str:str255)} var ItemType: integer; ItemBox: rect; ItemHdl: handle; begin GetDItem(TheDialog, item, ItemType, ItemHdl, ItemBox); SetIText(ItemHdl, str) end; function StringToReal (str: str255): real; var i, ndigits, StringLength: integer; c: char; n, m: real; negative, LeftOfPoint: boolean; begin negative := false; n := 0.0; LeftOfPoint := true; m := 0.1; ndigits := 0; StringLength := length(str); i := 0; repeat i := i + 1; until (str[i] in ['0'..'9', '-', '.']) or (i >= StringLength); c := str[i]; repeat if c = '-' then negative := true else if c = '.' then LeftOfPoint := false else if (c >= '0') and (c <= '9') then begin ndigits := ndigits + 1; if LeftOfPoint then n := n * 10.0 + ord(c) - ord('0') else begin n := n + (ord(c) - ord('0')) * m; m := m * 0.1; end; end; i := i + 1; if i <= StringLength then c := str[i]; until not (c in ['0'..'9', '-', '.']) or (i > StringLength); if ndigits = 0 then n := BadReal else if negative then n := -n; StringToReal := n; end; function GetDReal;{(TheDialog:DialogPtr; item:integer):extended} var str: str255; begin str := GetDString(TheDialog, item); GetDReal := StringToReal(str); end; procedure DrawLong;{(i:LongInt)} var str: str255; begin NumToString(i, str); DrawString(str); end; procedure RealToString;{(Val:extended; width,fwidth:integer; var Str:Str255)} {Does number to string conversion equivalent to write(val:width:fwidth).} {var} {form: DecForm;} begin if fwidth < 0 then begin if val < 1.0 then fwidth := 4 else if trunc(val) = val then fwidth := 0 else fwidth := 2; end; str := StringOf(val : width : fwidth); {Use LSP StringOf function because SANE Num2Str bombs out under A/UX} {form.digits := fwidth;} {form.style := FixedDecimal;} {Num2Str(form, val, DecStr(str));} {while length(Str) < width do begin} {str := concat(' ', Str)} {end;} end; procedure DrawReal;{(Val:extended; width,fwidth:integer)} {Displays a real(or integer) number at the current location in} {a form equivalent to write(val:width:fwidth) } var str: str255; begin RealToString(val, width, fwidth, str); DrawString(str); end; function GetInt;{(message:str255; default:integer):integer} const NumberID = 3; var mylog: DialogPtr; item: integer; temp: LongInt; begin ParamText(message, '', '', ''); mylog := GetNewDialog(3000, nil, pointer(-1)); SetDNum(MyLog, NumberID, default); SelIText(MyLog, NumberID, 0, 32767); OutlineButton(MyLog, ok, 16); repeat ModalDialog(nil, item); until (item = ok) or (item = cancel); if item = ok then begin temp := GetDNum(MyLog, NumberID); if (temp > -MaxInt) and (temp <= MaxInt) then GetInt := temp else begin SysBeep(1); temp := -MaxInt end; end else GetInt := -MaxInt; DisposDialog(mylog); end; function GetReal (message: str255; default: extended): extended; const NumberID = 3; var mylog: DialogPtr; item: integer; begin InitCursor; ParamText(message, '', '', ''); mylog := GetNewDialog(3000, nil, pointer(-1)); SetDReal(MyLog, NumberID, default, 2); SelIText(MyLog, NumberID, 0, 32767); OutlineButton(MyLog, ok, 16); repeat ModalDialog(nil, item); until (item = ok) or (item = cancel); if item = ok then GetReal := GetDReal(MyLog, NumberID) else GetReal := BadReal; DisposDialog(mylog); end; function OptionKeyDown;{:boolean} var KeyPtr: KeyPtrType; keys: array[0..3] of LongInt; begin KeyPtr := KeyPtrType(@keys); GetKeys(KeyPtr^); OptionKeyDown := (BAND(keys[1], 4)) <> 0; end; function ShiftKeyDown;{:boolean} var KeyPtr: KeyPtrType; keys: array[0..3] of LongInt; begin KeyPtr := KeyPtrType(@keys); GetKeys(KeyPtr^); ShiftKeyDown := (BAND(keys[1], 1)) <> 0; end; function ControlKeyDown;{:boolean} type KeyPtrType = ^KeyMap; var KeyPtr: KeyPtrType; keys: array[0..3] of LongInt; begin KeyPtr := KeyPtrType(@keys); GetKeys(KeyPtr^); ControlKeyDown := (BAND(keys[1], 8)) <> 0; end; function CommandPeriod;{:boolean} type KeyPtrType = ^KeyMap; var KeyPtr: KeyPtrType; keys: array[0..3] of LongInt; begin KeyPtr := KeyPtrType(@keys); GetKeys(KeyPtr^); CommandPeriod := (BAND(keys[1], $808000)) = $808000; end; function SpaceBarDown: boolean; var KeyPtr: KeyPtrType; keys: array[0..3] of LongInt; begin KeyPtr := KeyPtrType(@keys); GetKeys(KeyPtr^); SpaceBarDown := (BAND(keys[1], 512)) <> 0; end; procedure DrawSItem; {(itemnum, fontrqst, sizerqst: integer; d: dialogptr; s: str255)} {Draw a string item in a dialog box.} var r: rect; itype: integer; ignore: handle; begin getditem(d, itemnum, itype, ignore, r); textfont(fontrqst); textsize(sizerqst); textbox(pointer(ord(@s) + 1), length(s), r, TEJustRight); end; procedure SysResume; begin FlushEvents(EveryEvent, 0); ExitToShell; end; procedure beep; begin SysBeep(1) end; procedure PutMessage;{(str:str255)} var ignore: integer; begin InitCursor; ParamText(str, '', '', ''); Ignore := Alert(300, nil); end; function GetFontSize;{(item:integer):integer} var TempSize: integer; begin case item of 1: GetFontSize := 9; 2: GetFontSize := 10; 3: GetFontSize := 12; 4: GetFontSize := 14; 5: GetFontSize := 18; 6: GetFontSize := 24; 7: GetFontSize := 36; 8: GetFontSize := 48; 9: GetFontSize := 56; 10: GetFontSize := 72; 12: begin TempSize := GetInt('Font Size:', CurrentSize); if TempSize < 1 then TempSize := 1; if TempSize > 1000 then TempSize := 1000; GetFontSize := TempSize; end; end; end; procedure SetMenuItem; {(menuh:menuhandle; itemnum:integer; on:boolean)} {Enable or disable menuh's itemnum. } begin if on then EnableItem(menuh, itemnum) else DisableItem(menuh, itemnum); if ItemNum = 0 then DrawMenuBar; end; procedure CheckOnOffItem;{(MenuH:MenuHandle; item,fst,lst:Integer)} var i: integer; begin for i := fst to lst do if i = item then CheckItem(MenuH, i, true) else CheckItem(MenuH, i, false); end; procedure UpdateTextMenu; var size, i, MenuItem, FontID, item: integer; FontName: str255; FontFound, FoundIt: boolean; str: str255; begin FontFound := false; for item := 1 to NumFontItems do begin GetItem(FontMenuH, Item, FontName); GetFNum(FontName, FontID); if FontID = CurrentFontID then begin FontFound := true; CheckItem(FontMenuH, Item, True) end else CheckItem(FontMenuH, Item, false); end; if not FontFound then begin FoundIt := False; Item := 1; repeat GetItem(FontMenuH, Item, FontName); GetFNum(FontName, FontID); if FontID = Geneva then begin CheckItem(FontMenuH, Item, True); CurrentFontID := FontID; FoundIt := true; end; Item := Item + 1; until (Item > NumFontItems) or FoundIt; end; for i := 1 to 10 do begin size := GetFontSize(i); if RealFont(CurrentFontID, size) then SetItemStyle(SizeMenuH, i, [OutLine]) else SetItemStyle(SizeMenuH, i, []) end; NumToString(CurrentSize, str); str := concat('Other[', str, ']É'); SetItem(SizeMenuH, 12, str); for i := TxPlain to TxShadow do CheckItem(StyleMenuH, i, false); if CurrentStyle = [] then CheckItem(StyleMenuH, TxPlain, true) else begin if Bold in CurrentStyle then CheckItem(StyleMenuH, TxBold, true); if Italic in CurrentStyle then CheckItem(StyleMenuH, TxItalic, true); if Underline in CurrentStyle then CheckItem(StyleMenuH, TxUnderline, true); if Outline in CurrentStyle then CheckItem(StyleMenuH, TxOutline, true); if Shadow in CurrentStyle then CheckItem(StyleMenuH, Txshadow, true); end; case CurrentSize of 9: MenuItem := 1; 10: MenuItem := 2; 12: MenuItem := 3; 14: MenuItem := 4; 18: MenuItem := 5; 24: MenuItem := 6; 36: MenuItem := 7; 48: MenuItem := 8; 56: MenuItem := 9; 72: MenuItem := 10; otherwise MenuItem := 12; end; CheckOnOffItem(SizeMenuH, MenuItem, 1, 12); case TextJust of teJustLeft: MenuItem := LeftItem; teJustCenter: MenuItem := CenterItem; teJustRight: MenuItem := RightItem; end; CheckOnOffItem(TextMenuH, MenuItem, LeftItem, RightItem); if TextBack = NoBack then MenuItem := NoBackgroundItem else MenuItem := WithBackgroundItem; CheckOnOffItem(TextMenuH, MenuItem, NoBackgroundItem, WithBackgroundItem); end; procedure LoadLUT (table: MyCSpecArray); var i, entry, screen: integer; cPtr: ^cSpecArray; SaveDevice: GDHandle; begin if nExtraColors > 0 then begin entry := FirstExtraColorsEntry; for i := 1 to nExtraColors do begin table[entry].rgb := ExtraColors[i]; entry := entry + 1; end; end; for i := 1 to 254 do {Work around needed for 32-bit QuickDraw} with table[i].rgb do if (red = 0) and (green = 0) and (blue = 0) then begin red := 256; green := 256; blue := 256; end; cPtr := @table[1]; SaveDevice := GetGDevice; for screen := 1 to nMonitors do begin SetGDevice(Monitors[screen]); for i := 1 to 254 do begin ProtectEntry(i, false); ReserveEntry(i, false); end; SetEntries(1, 253, cPtr^); end; SetGDevice(SaveDevice); end; procedure RedrawLUTWindow; begin LoadLUT(info^.cTable); cheight := 256 + (2 + nExtraColors) * ExtraColorsHeight; SizeWindow(LUTWindow, cwidth, cheight, true); end; procedure Load256ColorCLUT; const Sat = -1; Val = -1; var i: integer; color: HSVColor; begin DisableDensitySlice; with info^ do begin for i := 0 to 255 do begin color.hue := i * 256; color.saturation := sat; color.value := val; HSV2RGB(color, ctable[i].rgb); end; LoadLUT(ctable); LUTMode := spectrum; end; IdentityFunction := false; end; function LoadPP2Palette: boolean; {Loads COLR resource from PixelPaint 2.0 palette file.} var i: integer; size: LongInt; h: Handle; PPColorTable: record ctSize: INTEGER; table: array[0..255] of RGBColor; end; begin h := GetResource('COLR', 999); size := GetHandleSize(handle(h)); if (ResError = NoErr) and (size = 1538) then with info^ do begin BlockMove(handle(h)^, @PPColorTable, SizeOf(PPColorTable)); with PPColorTable do begin for i := 0 to 255 do cTable[i].rgb := table[i]; end; LoadLUT(cTable); LUTMode := Custom; IdentityFunction := false; LoadPP2Palette := true; end else LoadPP2Palette := false; if h <> nil then DisposHandle(h); end; function LoadCLUTResource;{(id:integer):boolean} const ExpectedSize = 2056; var Size: LongInt; h: cTabHandle; MyColorTable: record ctSeed: LONGINT; transIndex: INTEGER; ctSize: INTEGER; ctTable: MyCSpecArray; end; begin DisableDensitySlice; h := GetCTable(id); size := GetHandleSize(handle(h)); if (ResError <> NoErr) or (size < ExpectedSize) then begin LoadCLUTResource := false; if id = PixelpaintID then begin if LoadPP2Palette then LoadCLUTResource := true; end; if h <> nil then DisposCTable(h); exit(LoadCLUTResource) end; if size > ExpectedSize then size := ExpectedSize; BlockMove(handle(h)^, @MyColorTable, size); DisposCTable(h); LoadLUT(MyColorTable.ctTable); with info^ do begin cTable := MyColorTable.ctTable; if id = AppleDefaultCLUT then LUTMode := AppleDefault else LUTMode := Custom; end; IdentityFunction := false; LoadCLUTResource := true; end; procedure DrawDensitySlice (OptionKey: boolean); var i, tRed: integer; begin with info^ do begin if OptionKey then begin ctable := SaveCTable^; end else for i := 0 to 255 do if (i >= SliceStart) and (i <= SliceEnd) then cTable[i].rgb := SliceColor else ctable[i].rgb := SaveCTable^[i].rgb; LoadLUT(cTable); end; end; procedure SelectLutTool; var tPort: GrafPtr; begin if (CurrentTool <> LutTool) and (CurrentTool <> Wand) then begin GetPort(tPort); SetPort(ToolWindow); InvalRect(ToolRect[CurrentTool]); InvalRect(ToolRect[LutTool]); CurrentTool := LutTool; isSelectionTool := false; SetPort(tPort); end; end; procedure EnableDensitySlice; begin if not DensitySlicing then begin new(SaveCTable); if SaveCTable <> nil then begin SaveCTable^ := info^.ctable; DrawDensitySlice(false); DensitySlicing := true; end; SelectLUTTool; end; end; procedure DisableDensitySlice; begin if DensitySlicing then begin DensitySlicing := false; with info^ do if lutMode = GrayScale then SetGrayScaleLUT else ctable := SaveCTable^; dispose(SaveCTable); LoadLUT(info^.cTable); end; end; procedure UpdateColors; var MaxStart, LastColor, i, v: integer; index: 0..MaxPseudoColorsLessOne; OptionKey: boolean; begin OptionKey := OptionKeyDown; DisableDensitySlice; with info^ do begin LastColor := ColorStart + nColors * ColorWidth - 1; for i := 0 to 255 do with cTable[255 - i].rgb do begin if (i < ColorStart) or (i > LastColor) then begin if OptionKey then begin v := bsl(i, 8); Red := v; Green := v; Blue := v; end else begin Red := 0; Green := 0; Blue := 0; end end else begin index := (i - ColorStart) div ColorWidth; if index < 0 then index := 0; if index > nColors - 1 then index := nColors - 1; Red := RedX[index]; Green := GreenX[index]; Blue := BlueX[index]; end; end; {for} LoadLUT(cTable); LUTMode := PseudoColor32; end; IdentityFunction := false; end; procedure LoadInputLoouupTable;{(address:ptr)} type ilutType = packed array[0..1023] of byte; ilutPtr = ^ilutType; var ilut: ilutPtr; i: integer; begin ilut := ilutPtr(address); if InvertVideo then begin for i := 0 to 255 do ilut^[i * 4] := i; ilut^[0] := 1; ilut^[255 * 4] := 254 end else begin for i := 0 to 255 do ilut^[i * 4] := 255 - i; ilut^[0] := 254; ilut^[255 * 4] := 1 end; end; procedure ResetQuickCapture; const ilutOffset = $90000; begin ControlReg^ := 1; {reset} while ControlReg^ < 0 do ; ChannelReg^ := VideoChannel * 64; while ControlReg^ < 0 do ; LoadInputLookupTable(Ptr(DTSlotBase + ilutOffset)); end; procedure GetLookupTable;{(VAR table:LookupTable)} var i, r, g, b: integer; GrayscaleImage: boolean; begin with info^ do begin if DensitySlicing then begin for i := 0 to 255 do if (i >= SliceStart) and (i <= SliceEnd) then begin if ThresholdToForeground then table[i] := ForegroundIndex else table[i] := i end else begin if NonThresholdToBackground then table[i] := BackgroundIndex else table[i] := i end; DisableDensitySlice; exit(GetLookupTable); end; if (LutMode = GrayScale) or (LutMode = CustomGrayscale) then for i := 0 to 255 do table[i] := 255 - BSR(cTable[i].RGB.red, 8) else begin table[0] := 0; for i := 1 to 254 do with cTable[i].RGB do table[i] := 255 - trunc(band(bsr(red, 8), 255) * 0.3 + band(bsr(green, 8), 255) * 0.59 + band(bsr(blue, 8), 255) * 0.11); table[255] := 255; end; end; {with} end; procedure wait;{(ticks:LongInt)} var SaveTicks: LongInt; begin SaveTicks := TickCount + ticks; repeat until TickCount > SaveTicks; end; procedure MakeLine (X1, Y1, X2, Y2: integer); var x: integer; v, temp: integer; begin with info^ do begin if not gmFixedSlope then begin DeltaX := X2 - X1; DeltaY := y2 - y1; end; if Deltax <> 0 then for X := X1 to X2 do with info^.cTable[255 - x].rgb do begin temp := (LongInt(DeltaY) * (x - x1)) div DeltaX + Y1; {Temporary variable needed to avoid range check} v := temp * 256; red := v; green := v; blue := v; end; end; end; procedure MakeHorizontalLine (X1, X2, Y: integer); var x: integer; v: integer; begin for X := X1 to X2 do with info^.cTable[255 - x].rgb do begin v := y * 256; red := v; green := v; blue := v; end; end; procedure SetGrayScaleLUT; begin with info^ do begin MakeHorizontalLine(0, p1x, 0); MakeLine(p1x, p1y, p2x, p2y); MakeHorizontalLine(p2x, 255, 255); LoadLUT(cTable); LUTMode := GrayScale; end; end; procedure CheckColorWidth; begin with info^ do if (ColorStart + ncolors * ColorWidth) > 256 then begin ColorWidth := (256 - ColorStart) div ncolors; if ColorWidth < 1 then ColorWidth := 1; end; end; procedure GetPaletteFromFile;{(fname:str255; vnum:integer)} var PaletteHeader: ColorArray; err, f: integer; size: LongInt; begin err := FSOpen(fname, vnum, f); with info^ do begin size := SizeOf(ColorArray); err := FSRead(f, size, @PaletteHeader); nColors := PaletteHeader[0]; if nColors > MaxPseudocolors then nColors := MaxPseudoColors; ColorStart := PaletteHeader[1]; ColorWidth := PaletteHeader[2]; CheckColorWidth; with PaletteRec do begin err := FSRead(f, size, @RedData); err := FSRead(f, size, @GreenData); err := FSRead(f, size, @BlueData); end; end; err := fsclose(f); PaletteName := fname; end; procedure GetDefaultPalette; var Size: LongInt; pHandle: handle; i: integer; begin with info^ do begin ncolors := 0; pHandle := GetResource('CPAL', 1000); if (ResError <> noErr) or (pHandle = nil) then begin beep; if pHandle <> nil then ReleaseResource(pHandle); exit(GetDefaultPalette) end; Size := GetHandleSize(pHandle); if size = SizeOF(PaletteRec) then begin BlockMove(pHandle^, @PaletteRec, size); ncolors := PaletteRec.NumberOfColors; end; for i := 0 to MaxPseudoColorsLessOne do with PaletteRec do begin RedX[i] := RedData[i] * 255; GreenX[i] := GreenData[i] * 255; BlueX[i] := BlueData[i] * 255; end; LUTMode := PseudoColor32; end; ReleaseResource(pHandle); end; procedure InitColor;{(fname:str255; vnum:integer)} var i: integer; begin with info^ do begin if fname = 'Default' then GetDefaultPalette else begin GetPaletteFromFile(fname, vnum); LUTMode := PseudoColor32; end; for i := 0 to ncolors - 1 do with PaletteRec do begin RedX[i] := RedData[i] * 255; GreenX[i] := GreenData[i] * 255; BlueX[i] := BlueData[i] * 255; end; end; end; function GetScrapCount;{:integer} var ScrapInfo: PScrapStuff; begin ScrapInfo := InfoScrap; GetScrapCount := ScrapInfo^.ScrapCount; end; procedure DisplayText (update: boolean); var tPort: GrafPtr; i, hstart, width, ff: integer; MaskRect: rect; p1, p2: point; begin if (info = NoInfo) or (not IsInsertionPoint) then exit(DisplayText); if update then Undo; GetPort(tPort); SetPort(GrafPtr(Info^.osPort)); TextFont(CurrentFontID); TextFace(CurrentStyle); TextSize(CurrentSize); if TextBack = NoBack then TextMode(SrcOr) else TextMode(SrcCopy); width := StringWidth(TextStr); case TextJust of teJustLeft: hstart := TextStart.h; teJustCenter: hstart := TextStart.h - width div 2; teJustRight: hstart := TextStart.h - width; end; if hstart < 0 then hstart := 0; MoveTo(hstart, TextStart.v); DrawString(TextStr); GetPen(InsertionPoint); ff := CurrentSize * 2; p1.h := hstart - ff; p1.v := TextStart.v - CurrentSize; p2.h := TextStart.h + width + ff; p2.v := TextStart.v + CurrentSize div 3; Pt2Rect(p1, p2, MaskRect); UpdateScreen(MaskRect); SetPort(tPort); Info^.changes := true; end; procedure SetForegroundColor;{(color:integer)} var tPort: GrafPtr; begin if (color >= 0) and (color <= 255) then with info^ do begin ForegroundIndex := color; GetPort(tPort); SetPort(ToolWindow); InvalRect(ToolRect[brush]); if LUTMode = PseudoColor32 then CurrentColorIndex := GetColorIndex; if osPort <> nil then begin SetPort(GrafPtr(osPort)); pmForeColor(ForegroundIndex); end; SetPort(tPort); if isInsertionPoint then DisplayText(true); end; end; procedure SetBackgroundColor;{(color:integer)} var tPort: GrafPtr; begin if (color >= 0) and (color <= 255) then with info^ do begin BackgroundIndex := color; GetPort(tPort); SetPort(ToolWindow); InvalRect(ToolRect[eraser]); if osPort <> nil then begin SetPort(GrafPtr(osPort)); pmBackColor(BackgroundIndex); end; SetPort(tPort); if isInsertionPoint then DisplayText(true); end; end; function GetColorIndex;{:integer} var CLUTIndex: LongInt; begin CLUTIndex := 255 - ForegroundIndex; with info^ do if (CLUTIndex < ColorStart) or (CLUTIndex > (ColorStart + nColors * ColorWidth)) then begin GetColorIndex := NoColor end else GetColorIndex := (CLUTIndex - ColorStart) div ColorWidth; end; procedure OffScreenToScreenRect;{(VAR r:rect)} var p1, p2: point; begin with r do begin p1.h := left; p1.v := top; p2.h := right; p2.v := bottom; OffScreenToScreen(p1); OffScreenToScreen(p2); Pt2Rect(p1, p2, r); end; end; procedure ScreenToOffscreen;{(VAR loc:point)} begin with loc, Info^ do begin h := SrcRect.left + trunc(h / magnification); v := SrcRect.top + trunc(v / magnification); end; end; procedure OffscreenToScreen;{(VAR loc:point)} begin with loc, Info^ do begin h := trunc((h - SrcRect.left) * magnification); v := trunc((v - SrcRect.top) * magnification); end; end; procedure UpdateScreen;{(MaskRect:rect)} {Refreshes the portion of the screen defined by} {MaskRect, where MaskRect is defined in offscreen coordinates.} var tPort: GrafPtr; imag: integer; begin OffScreenToScreenRect(MaskRect); with Info^ do if info <> NoInfo then begin getPort(tPort); SetPort(wptr); pmForeColor(BlackIndex); pmBackColor(WhiteIndex); imag := trunc(magnification); InsetRect(MaskRect, -imag * 2 * LineWidth, -imag * 2 * LineWidth); InsetRect(MaskRect, 0, 0); RectRgn(MaskRgn, MaskRect); hlock(handle(osPort^.portPixMap)); hlock(handle(CGrafPort(wptr^).PortPixMap)); CopyBits(BitMapHandle(osPort^.PortPixMap)^^, BitMapHandle(CGrafPort(wptr^).PortPixMap)^^, SrcRect, wrect, SrcCopy, MaskRgn); hunlock(handle(osPort^.portPixMap)); hunlock(handle(CGrafPort(wptr^).PortPixMap)); SetPort(tPort); end; end; procedure RestoreRoi; begin with Info^ do begin SetupUndo; if RoiShowing then UpdateScreen(RoiRect); roiType := NoInfo^.roiType; RoiRect := NoInfo^.RoiRect; CopyRgn(NoInfo^.roiRgn, roiRgn); RoiShowing := true; measuring := false; WhatToUndo := NothingToUndo; end; end; procedure Undo; var SrcPtr: ptr; line: integer; begin if info^.PixMapSize <> CurrentUndoSize then exit(Undo); if UndoFromClip then begin if info^.PixMapSize > ClipBufSize then exit(Undo); SrcPtr := ClipBuf; end else SrcPtr := UndoBuf; with info^ do BlockMove(SrcPtr, PicBaseAddr, PixMapSize); if UndoFromClip and RestoreUndoBuf then with info^ do BlockMove(SrcPtr, UndoBuf, PixMapSize); if RedoSelection then RestoreRoi; end; function MyGetPixel;{(h,v:integer):integer} var offset: LongInt; p: ptr; begin with Info^ do begin if (h < 0) or (v < 0) or (h >= PixelsPerLine) or (v >= nlines) then begin MyGetPixel := WhiteIndex; exit(MyGetPixel); end; offset := LongInt(v) * BytesPerRow + h; if offset >= PixMapSize then exit(MyGetPixel); p := ptr(ord4(PicBaseAddr) + offset); MyGetPixel := BAND(p^, 255); end; end; procedure PutPixel;{(h,v,value:integer)} type uptr = ^UnsignedByte; var offset: LongInt; p: ptr; begin with Info^ do begin if (h < 0) or (v < 0) or (h >= PixelsPerLine) or (v >= nlines) then exit(PutPixel); offset := LongInt(v) * BytesPerRow + h; p := ptr(ord4(PicBaseAddr) + offset); p^ := BAND(value, 255); end; end; procedure GetLine;{(h,v,count:integer; VAR line:LineType)} var offset: LongInt; p: ptr; begin with Info^ do begin if (h < 0) or (v < 0) or ((h + count) > PixelsPerLine) or (v >= nlines) then begin line := BlankLine; exit(GetLine); end; offset := LongInt(v) * BytesPerRow + h; p := ptr(ord4(PicBaseAddr) + offset); BlockMove(p, @line, count); end; end; procedure GetColumn;{(hstart,vstart,count:integer; VAR data:LineType)} var i, v: integer; begin if count > MaxPixelsPerLine then count := MaxPixelsPerLine; v := vstart; for i := 0 to count - 1 do begin data[i] := MyGetPixel(hstart, v); v := v + 1; end; end; procedure PutColumn;{(hstart,vstart,count:integer; VAR data:LineType)} var i, v: integer; begin if count > MaxPixelsPerLine then count := MaxPixelsPerLine; v := vstart; for i := 0 to count - 1 do begin PutPixel(hstart, v, data[i]); v := v + 1; end; end; procedure PutLine;{(h,v,count:integer; VAR line:LineType)} var offset: LongInt; p: ptr; begin with Info^ do begin if (h < 0) or (v < 0) or (v >= nlines) then exit(PutLine); if (h + count) > PixelsPerLine then count := PixelsPerLine - h; offset := LongInt(v) * BytesPerRow + h; p := ptr(ord4(PicBaseAddr) + offset); BlocKMove(@line, p, count); end; end; procedure Show1Value (rvalue, CalibratedValue: extended); var tPort: GrafPtr; hstart, vstart, ivalue: integer; begin hstart := ValuesHStart; vstart := ValuesVStart; GetPort(tPort); SetPort(ResultsWindow); TextSize(9); TextFont(Monaco); TextMode(SrcCopy); MoveTo(xValueLoc, vstart); if CalibratedValue <> NoValue then begin DrawReal(CalibratedValue, 5, 2); DrawString(' ('); DrawReal(rvalue, 3, 0); DrawString(')'); end else DrawReal(rvalue, 6, 2); DrawString(' '); SetPort(tPort); end; procedure Show2CalibratedValues; {(x, y: LongInt; ShowUncalibrated: boolean)} var tPort: GrafPtr; hstart, vstart, ivalue: integer; begin hstart := ValuesHStart; vstart := ValuesVStart; GetPort(tPort); SetPort(ResultsWindow); TextSize(9); TextFont(Monaco); TextMode(SrcCopy); MoveTo(xValueLoc, vstart); DrawLong(x); DrawString(' '); MoveTo(yValueLoc, vstart + 10); if info^.Calibrated then begin DrawReal(cvalue[y], 5, 2); if ShowUncalibrated then begin DrawString(' ('); DrawLong(y); DrawString(')'); end; end else DrawLong(y); DrawString(' '); SetPort(tPort); end; procedure Show2Values (current, total: LongInt); var tPort: GrafPtr; hstart, vstart, ivalue: integer; begin hstart := ValuesHStart; vstart := ValuesVStart; GetPort(tPort); SetPort(ResultsWindow); TextSize(9); TextFont(Monaco); TextMode(SrcCopy); MoveTo(xValueLoc, vstart); DrawLong(current); DrawString(' '); MoveTo(yValueLoc, vstart + 10); DrawLong(total); DrawString(' '); SetPort(tPort); end; procedure DrawDimension (x: integer); begin with info^ do begin if SpatialScale <> 0.0 then begin DrawReal(x / SpatialScale, 5, 2); DrawString(units); DrawString(' ('); DrawReal(x, 3, 0); DrawString(')') end else DrawLong(x); DrawString(' '); end; end; procedure Show3Values;{(hloc,vloc,ivalue:LongInt)} var tPort: GrafPtr; hstart, vstart: integer; begin with info^ do begin hstart := ValuesHStart; vstart := ValuesVStart; GetPort(tPort); SetPort(ResultsWindow); TextSize(9); TextFont(Monaco); TextMode(SrcCopy); if hloc < 0 then hloc := -hloc; MoveTo(xValueLoc, vstart); DrawDimension(hloc); if InvertYCoordinates and (ivalue >= 0) then vloc := PicRect.bottom - vloc - 1; if vloc < 0 then vloc := -vloc; MoveTo(yValueLoc, vstart + 10); DrawDimension(vloc); DrawString(' '); if ivalue >= 0 then begin MoveTo(zValueLoc, vstart + 20); if Calibrated then begin DrawReal(cvalue[ivalue], 5, 2); DrawString(' ('); DrawLong(ivalue); DrawString(')'); end else DrawLong(ivalue); end; DrawString(' '); SetPort(tPort); end; end; procedure Show3RealValues;{(X,Y:LongInt; Z:extended)} var tPort: GrafPtr; hstart, vstart, ivalue: integer; begin with info^ do begin hstart := ValuesHStart; vstart := ValuesVStart; GetPort(tPort); SetPort(ResultsWindow); TextSize(9); TextFont(Monaco); TextMode(SrcCopy); MoveTo(xValueLoc, vstart); DrawDimension(x); MoveTo(yValueLoc, vstart + 10); DrawDimension(y); MoveTo(zValueLoc, vstart + 20); if SpatialScale <> 0.0 then begin DrawReal(z / SpatialScale, 5, 2); DrawString(units); DrawString(' ('); DrawReal(z, 1, 2); DrawString(')') end else DrawReal(z, 1, 2); DrawString(' '); SetPort(tPort); end; end; procedure PutChar;{(c:char)} begin if TextBufSize < MaxTextBufSize then begin TextBufSize := TextBufSize + 1; TextBufP^[TextBufSize] := c; if c = cr then begin TextBufColumn := 0; TextBufLineCount := TextBufLineCount + 1 end else TextBufColumn := TextBufColumn + 1; end; end; procedure PutTab; begin if not printing then PutChar(tab) end; procedure PutString (str: str255); var i: integer; begin for i := 1 to length(str) do begin if TextBufSize < MaxTextBufSize then TextBufSize := TextBufSize + 1; TextBufP^[TextBufSize] := str[i]; TextBufColumn := TextBufColumn + 1; end; end; procedure PutFString (str: str255; FieldWidth: integer); var LeadingSpaces: integer; begin LeadingSpaces := FieldWidth - length(str); if LeadingSpaces > 0 then str := concat(copy(' ', 1, LeadingSpaces), str); PutString(str); end; procedure PutReal;{(n:extended; width,fwidth:integer)} var str: str255; begin RealToString(n, width, fwidth, str); PutString(str); end; procedure PutLong (n: LongInt; FieldWidth: integer); var str: str255; LeadingSpaces: integer; begin NumToString(n, str); LeadingSpaces := FieldWidth - length(str); if LeadingSpaces > 0 then str := concat(copy(' ', 1, LeadingSpaces), str); PutString(str); end; procedure CopyResultsToBuffer (FirstCount, LastCount: integer; Headings: boolean); var i, column, fwidth: integer; m: MeasurementTypes; procedure PutSequenceNumber; begin PutLong(i, 4); PutChar('.'); PutTab; end; procedure PutUnits; begin if info^.SpatialScale <> 0.0 then begin PutString(' ('); PutString(info^.Units); PutString(')') end else PutString('(Pixels)'); PutChar(cr); PutChar(cr); end; procedure PutTabDelimeter; begin Column := Column + 1; if Column <> nListColumns then PutTab; end; begin if mCount < 1 then begin TextBufSize := 0; TextBufLineCount := 0; exit(CopyResultsToBuffer); end; ShowWatch; Headings := Headings or OptionKeyWasDown; TextBufSize := 0; TextBufColumn := 0; TextBufLineCount := 0; nListColumns := 0; for m := AreaM to StdDevM do if m in Measurements then nListColumns := nListColumns + 1; if (xyLocM in measurements) or (nPoints > 0) then nListColumns := nListColumns + 2; if ModeM in measurements then nListColumns := nListColumns + 1; if (LengthM in measurements) or (nLengths > 0) then nListColumns := nListColumns + 1; if MajorAxisM in measurements then nListColumns := nListColumns + 1; if MinorAxisM in measurements then nListColumns := nListColumns + 1; if (AngleM in measurements) or (nAngles > 0) then nListColumns := nListColumns + 1; if IntDenM in measurements then nListColumns := nListColumns + 2; if MinMaxM in measurements then nListColumns := nListColumns + 2; with info^ do begin fwidth := FieldWidth; if Headings and (FirstCount = 1) then begin PutFString(' ', 5); PutTabDelimeter; if AreaM in measurements then begin PutFString('Area', fwidth); PutTabDelimeter; end; if MeanM in measurements then begin PutFString('Mean', fwidth); PutTabDelimeter; end; if StdDevM in measurements then begin PutFString('S.D.', fwidth); PutTabDelimeter; end; if (xyLocM in measurements) or (nPoints > 0) then begin PutFString('X', fwidth); PutTabDelimeter; PutFString('Y', fwidth); PutTabDelimeter; end; if ModeM in measurements then begin PutFString('Mode', fwidth); PutTabDelimeter; end; if (LengthM in measurements) or (nLengths > 0) then begin PutFString('Length', fwidth); PutTabDelimeter; end; if MajorAxisM in measurements then begin PutFString(MajorLabel, fwidth); PutTabDelimeter; end; if MinorAxisM in measurements then begin PutFString(MinorLabel, fwidth); PutTabDelimeter; end; if (AngleM in measurements) or (nAngles > 0) then begin PutFString('Angle', fwidth); PutTabDelimeter; end; if IntDenM in measurements then begin PutFString('Int.Den.', fwidth + 2); PutTabDelimeter; PutFString('Back.', fwidth); PutTabDelimeter; end; if MinMaxM in measurements then begin PutFString('Min', fwidth); PutTabDelimeter; PutFString('Max', fwidth); PutTabDelimeter; end; PutChar(cr); PutChar(cr); end; for i := FirstCount to LastCount do begin column := 0; if Headings then PutSequenceNumber; if AreaM in measurements then begin PutReal(mArea^[i], fwidth, precision); PutTabDelimeter; end; if MeanM in measurements then begin PutReal(mean^[i], fwidth, precision); PutTabDelimeter; end; if StdDevM in measurements then begin PutReal(sd^[i], fwidth, precision); PutTabDelimeter; end; if (xyLocM in measurements) or (nPoints > 0) then begin PutReal(xcenter^[i], fwidth, precision); PutTab; PutReal(ycenter^[i], fwidth, precision); PutTabDelimeter; end; if ModeM in measurements then begin PutReal(mode^[i], fwidth, precision); PutTabDelimeter; end; if (LengthM in measurements) or (nLengths > 0) then begin PutReal(plength^[i], fwidth, precision); PutTabDelimeter; end; if MajorAxisM in measurements then begin PutReal(MajorAxis^[i], fwidth, precision); PutTabDelimeter; end; if MinorAxisM in measurements then begin PutReal(MinorAxis^[i], fwidth, precision); PutTabDelimeter; end; if (AngleM in measurements) or (nAngles > 0) then begin PutReal(orientation^[i], fwidth, precision); PutTabDelimeter; end; if IntDenM in measurements then begin PutReal(IntegratedDensity^[i], fwidth + 2, precision); PutTabDelimeter; PutReal(idBackground^[i], fwidth, precision); PutTabDelimeter; end; if MinMaxM in measurements then begin PutReal(mMin^[i], fwidth, precision); PutTabDelimeter; PutReal(mMax^[i], fwidth, precision); PutTabDelimeter; end; PutChar(cr); end; {for} end; {with} end; procedure ShowWatch; begin SetCursor(watch); end; procedure UpdatePicWindow; var tPort: GrafPtr; begin if info <> NoInfo then with Info^ do begin getPort(tPort); SetPort(wptr); pmForeColor(BlackIndex); pmBackColor(WhiteIndex); hlock(handle(osPort^.portPixMap)); hlock(handle(CGrafPort(wptr^).PortPixMap)); CopyBits(BitMapHandle(osPort^.portPixMap)^^, BitMapHandle(CGrafPort(wptr^).PortPixMap)^^, SrcRect, wrect, SrcCopy, nil); hunlock(handle(osPort^.portPixMap)); hunlock(handle(CGrafPort(wptr^).PortPixMap)); SetPort(tPort); RoiUpdateTime := 0; end; end; procedure DoOperation;{(Operation:OpType)} var tPort: GrafPtr; loc: point; width, height: integer; tRect: rect; begin GetPort(tPort); with Info^ do begin changes := true; SetPort(GrafPtr(osPort)); PenNormal; PenSize(LineWidth, LineWidth); case Operation of InvertOp: InvertRgn(roiRgn); PaintOp: PaintRgn(roiRgn); FrameOp: FrameRgn(roiRgn); EraseOp: EraseRgn(roiRgn); PasteOp: Paste; otherwise end; if not RoiShowing then UpdateScreen(RoiRect); if PixMapSize > UndoBufSize then OpPending := false; end; SetPort(tPort); end; procedure SaveRoi; begin with info^ do if RoiType <> noRoi then begin NoInfo^.roiType := roiType; NoInfo^.RoiRect := RoiRect; CopyRgn(roiRgn, NoInfo^.roiRgn); end; end; procedure KillRoi; begin with info^ do begin if RoiShowing then begin if OpPending then begin OpPending := false; DoOperation(CurrentOp); end; SaveRoi; RoiShowing := false; UpdateScreen(RoiRect); end; RoiType := NoRoi; RoiUpdateTime := 0; end; end; procedure Paste; var srcPort: cGrafPtr; begin if info = NoInfo then begin beep; exit(Paste) end; with Info^ do begin if not RoiShowing then exit(Paste); if PasteTransferMode = SrcCopy then begin pmForeColor(BlackIndex); pmBackColor(WhiteIndex); end; srcPort := ClipBufInfo^.osPort; if LivePasteMode then if (WhatsOnClip = CameraPic) and (QuickCaptureInfo <> nil) and (PictureType <> QuickCaptureType) then begin ControlReg^ := BitAnd($80, 255); {Start frame capture} while ControlReg^ < 0 do ; {Wait for it to complete} srcPort := qcPort; end; hlock(handle(srcPort^.portPixMap)); hlock(handle(osPort^.portPixMap)); CopyBits(BitMapHandle(srcPort^.portPixMap)^^, BitMapHandle(osPort^.PortPixMap)^^, ClipBufInfo^.RoiRect, RoiRect, PasteTransferMode, roiRgn); hunlock(handle(srcPort^.portPixMap)); hunlock(handle(osPort^.PortPixMap)); if PasteTransferMode = SrcCopy then begin pmForeColor(ForegroundIndex); pmBackColor(BackgroundIndex); end; end; end; procedure ShowRoi; begin with info^ do if RoiType <> NoRoi then begin SetupUndo; RoiShowing := true; end; end; procedure SetupUndo; var line: integer; begin if info = NoInfo then begin CurrentUndoSize := 0; exit(SetupUndo) end; if info^.PixMapSize > UndoBufSize then begin CurrentUndoSize := 0; WhatToUndo := NothingToUndo; exit(SetupUndo) end; with info^ do begin if OpPending then begin DoOperation(CurrentOp); OpPending := false; end; CurrentUndoSize := PixMapSize; BlockMove(PicBaseAddr, UndoBuf, PixMapSize); UndoFromClip := false; RedoSelection := false; end; end; procedure SetupUndoFromClip; var line: integer; begin if info = NoInfo then begin CurrentUndoSize := 0; WhatToUndo := NothingToUndo; exit(SetupUndoFromClip) end; if info^.PixMapSize > ClipBufSize then begin CurrentUndoSize := 0; WhatToUndo := NothingToUndo; exit(SetupUndoFromClip) end; with info^ do begin if OpPending then begin DoOperation(CurrentOp); OpPending := false; end; CurrentUndoSize := PixMapSize; BlockMove(PicBaseAddr, ClipBuf, PixMapSize); end; WhatsOnClip := nothing; UndofromClip := true; RedoSelection := false; end; function NoSelection;{:boolean} begin if Info = NoInfo then begin beep; NoSelection := true; exit(NoSelection); end; if not Info^.RoiShowing then begin PutMessage('Please use the Selection Tool to make a selection or use the Select All command.'); macro := false; end; NoSelection := not Info^.RoiShowing; end; function NotRectangular;{:boolean} begin with info^ do if RoiShowing and (RoiType <> RectRoi) then begin PutMessage('This operation requires a rectangular selection.'); NotRectangular := true; macro := false; end else NotRectangular := false; end; function NotInBounds;{:boolean} begin NotInBounds := false; with info^, info^.RoiRect do if RoiShowing then if (left < 0) or (top < 0) or (right > PicRect.right) or (bottom > PicRect.bottom) then begin PutMessage('This operation requires the selection to be entirely within the image.'); NotInBounds := true; macro := false; end; end; function NoUndo: boolean; var ImageTooLarge: boolean; begin with info^ do ImageTooLarge := (PixMapSize > ClipBufSize) or (PixMapSize > UndoBufSize); if ImageTooLarge then PutMessage('This operation requires that the Undo and Clipboard buffers be at least as large as the image.'); NoUndo := ImageTooLarge; end; procedure PutMemoryAlert; begin PutMessage('Sorry, but there is not enough memory available to open this image. Try closing some windows.'); macro := false; end; procedure CompactMemory; var size: LongInt; TempInfo: InfoPtr; i: integer; begin for i := 1 to nPics do begin TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon); hunlock(TempInfo^.PicBaseHandle) end; size := 4000000; PurgeMem(size); size := CompactMem(size); for i := 1 to nPics do begin TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon); with TempInfo^ do begin hlock(PicBaseHandle); PicBaseAddr := StripAddress(PicBaseHandle^); osPort^.PortPixMap^^.BaseAddr := PicBaseAddr; end; end; end; function GetImageMemory (SaveInfo: infoPtr; var PicBaseHandle: handle; double: boolean): ptr; {Allocates memory for the PixMap of new image windows. SaveInfo points to the InfoRec of the previous window.} {A handle is used, rather than a pointer, since NewPtr(particularly on the ci and fx) is rediculously slow.} {Would you believe up to 10 seconds when many windows are open?} const MinFree = 100000; var h: handle; FreeMem, NeededSize: LongInt; begin with info^ do begin if odd(PixelsPerLine) then BytesPerRow := PixelsPerLine + 1 else BytesPerRow := PixelsPerLine; PixMapSize := LongInt(nlines) * BytesPerRow; ImageSize := LongInt(nlines) * PixelsPerLine; NeededSize := PixMapSize; if double then NeededSize := NeededSize * 2; h := NewHandle(NeededSize); end; FreeMem := MaxBlock; if (h = nil) or (FreeMem < MinFree) then begin if h <> nil then DisposHandle(h); CompactMemory; h := NewHandle(NeededSize); FreeMem := MaxBlock; end; if (h = nil) or (FreeMem < MinFree) then begin if h <> nil then DisposHandle(h); PutMemoryAlert; DisposPtr(pointer(Info)); Info := SaveInfo; GetImageMemory := nil; exit(GetImageMemory); end; PicBaseHandle := h; hlock(PicBaseHandle); GetImageMemory := StripAddress(PicBaseHandle^); end; function OldGetMemory (Size: LongInt; SaveInfo: infoPtr; var PicBaseHandle: handle): ptr; const MinFree = 100000; var h1, h2: handle; begin h1 := NewHandle(size); h2 := NewHandle(MinFree); if (h1 = nil) or (h2 = nil) then begin if h1 <> nil then DisposHandle(h1); if h2 <> nil then DisposHandle(h2); CompactMemory; h1 := NewHandle(size); h2 := NewHandle(MinFree); end; if (h1 = nil) or (h2 = nil) then begin if h1 <> nil then DisposHandle(h1); if h2 <> nil then DisposHandle(h2); PutMemoryAlert; DisposPtr(pointer(Info)); Info := SaveInfo; OldGetMemory := nil; exit(OldGetMemory); end; DisposHandle(h2); PicBaseHandle := h1; hlock(PicBaseHandle); OldGetMemory := PicBaseHandle^; end; procedure UpdateAnalysisMenu; var ShowItems: boolean; i: integer; begin ShowItems := Info <> NoInfo; SetMenuItem(AnalyzemenuH, MeasureItem, ShowItems); SetMenuItem(AnalyzemenuH, AnalyzeItem, ShowItems); SetMenuItem(AnalyzemenuH, HistogramItem, ShowItems); SetMenuItem(AnalyzemenuH, PlotItem, ShowItems); SetMenuItem(AnalyzemenuH, Plot3DItem, ShowItems); SetMenuItem(AnalyzemenuH, RedoItem, mCount > 0); SetMenuItem(AnalyzemenuH, DeleteItem, mCount > 0); SetMenuItem(AnalyzemenuH, RestoreItem, ShowItems and (NoInfo^.RoiType <> NoRoi)); SetMenuItem(AnalyzemenuH, MarkItem, info^.RoiShowing); end; procedure ExtendWindowsMenu;{(fname:str255; size:LongInt; wptr:WindowPtr)} var str, SizeStr: str255; begin if nPics < MaxPics then begin nPics := nPics + 1; PicWindow[nPics] := wptr; NumToString(size div 1024, SizeStr); str := concat(fname, ' ', SizeStr, 'K'); AppendMenu(WindowsMenuH, ' '); SetItem(WindowsMenuH, nPics + WindowsMenuItems, str); InsertMenu(WindowsMenuH, 0); end; end; procedure InvertGrayLevels; begin with info^ do begin calibrated := true; nCoefficients := 2; fit := StraightLine; Coefficient[1] := 255.0; Coefficient[2] := -1.0 end; end; procedure MakeNewWindow;{(name:str255)} var wwidth, wheight, wleft, wtop, i: integer; tPort: GrafPtr; rgb: RGBColor; err: OSErr; begin with Info^ do begin wleft := PicLeft; wtop := PicTop; PicLeft := PicLeft + hPicOffset; PicTop := PicTop + vPicOffset; if ((PicLeft + round(0.75 * PixelsPerLine)) > ScreenWidth) or ((PicTop + round(0.75 * nlines)) > ScreenHeight) then begin PicLeft := PicLeftBase; PicTop := PicTopBase; end; wwidth := PixelsPerLine; if (wleft + wwidth) > ScreenWidth then wwidth := ScreenWidth - wleft - 5; wheight := nlines; if (wtop + wheight) > ScreenHeight then wheight := ScreenHeight - wtop - 5; SetRect(wrect, wleft, wtop, wleft + wwidth, wtop + wheight); wptr := NewCWindow(nil, wrect, name, true, DocumentProc + ZoomDocProc, nil, true, 0); GetPort(tPort); SetPort(wptr); SetPalette(wptr, ExplicitPalette, false); pmForeColor(BlackIndex); pmBackColor(WhiteIndex); SetRect(wrect, 0, 0, wwidth, wheight); SetRect(PicRect, 0, 0, PixelsPerLine, nlines); SelectWindow(wptr); WindowPeek(wptr)^.WindowKind := PicKind; WindowPeek(wptr)^.RefCon := ord4(Info); title := name; ExtendWindowsMenu(name, ImageSize, wptr); PicNum := nPics; new(osPort); OpenCPort(osPort); with osPort^ do begin with PortPixMap^^ do begin BaseAddr := PicBaseAddr; bounds := PicRect; end; PortRect := PicRect; RectRgn(visRgn, PicRect); PortPixMap^^.RowBytes := BitOr(BytesPerRow, $8000); end; SetPalette(WindowPtr(osPort), ExplicitPalette, false); pmForeColor(ForegroundIndex); pmBackColor(BackgroundIndex); SetPort(tPort); SrcRect := wrect; magnification := 1.0; RoiShowing := false; roiType := NoRoi; initwrect := wrect; savewrect := wrect; SaveSrcRect := SrcRect; SaveMagnification := magnification; savehloc := wleft; savevloc := wtop; roiRgn := NewRgn; NewPic := true; ScaleToFitWindow := false; OpPending := false; Changes := false; WindowState := NormalWindow; if not Calibrated and UseZeroForBlack then InvertGrayLevels; Revertable := false; end; WhatToUndo := NothingToUndo; end; procedure MakeRegion; begin with info^ do begin OpenRgn; case RoiType of OvalRoi: FrameOval(RoiRect); RoundRectRoi: FrameRoundRect(RoiRect, OvalSize, OvalSize); RectRoi: FrameRect(RoiRect); otherwise end; CloseRgn(roiRgn) end; end; procedure SelectAll;{(visible:boolean)} var loc: point; tPort: GrafPtr; begin KillRoi; with Info^ do begin RoiType := RectRoi; RoiRect := PicRect; MakeRegion; if visible then begin SetupUndo; WhatToUndo := NothingToUndo; RoiShowing := true; if (magnification > 1.0) and not ScaleToFitWindow then Unzoom; PreviousTool := CurrentTool; CurrentTool := SelectionTool; isSelectionTool := true; GetPort(tPort); SetPort(ToolWindow); EraseRect(ToolRect[PreviousTool]); EraseRect(ToolRect[CurrentTool]); InvalRect(ToolRect[PreviousTool]); InvalRect(ToolRect[CurrentTool]); SetPort(tPort); end; IsInsertionPoint := false; measuring := false; end; {with} end; procedure KillOperation; begin if OpPending then with info^ do if info <> NoInfo then begin DoOperation(CurrentOp); RoiShowing := false; UpdateScreen(RoiRect); OpPending := false; end; end; function NewPicWindow;{(name:str255; width,height:integer):boolean} var iptr: ptr; lptr: ^LongInt; SaveInfo: InfoPtr; NeededSize: LongInt; begin NewPicWindow := false; KillOperation; DisableDensitySlice; SaveInfo := Info; iptr := NewPtr(SizeOf(PicInfo)); if iptr = nil then begin DisposPtr(iptr); PutMemoryAlert; exit(NewPicWindow); end; Info := pointer(iptr); info^ := SaveInfo^; with Info^ do begin nlines := height; PixelsPerLine := width; if name = 'Camera' then begin PictureType := QuickCaptureType; QuickCaptureInfo := info; end; PicBaseAddr := GetImageMemory(SaveInfo, PicBaseHandle, false); if PicBaseAddr = nil then exit(NewPicWindow); PicLeft := PicLeftBase; PicTop := PicTopBase; MakeNewWindow(name); if name <> 'Camera' then PictureType := NewPicture; SelectAll(false); DoOperation(EraseOp); RoiType := NoRoi; changes := false; BinaryPic := false; end; NewPicWindow := true; end; procedure EraseScreen; begin SetPort(GrafPtr(CScreenPort)); with CScreenPort^ do begin HideCursor; pmBackColor(BackgroundIndex); EraseRect(portPixMap^^.Bounds); pmBackColor(WhiteIndex); end; end; procedure RestoreScreen; var GrayRgn: RgnHandle; rptr: rhptr; wp: ^WindowPtr; begin rptr := rhptr(GrayRgnGlobal); GrayRgn := rptr^; wp := pointer(GhostWindow); wp^ := WindowPtr(nil); PaintBehind(WindowPeek(FrontWindow), GrayRgn); wp^ := PasteControl; DrawMenuBar; end; procedure ScaleToFit; var trect: rect; begin if digitizing then exit(ScaleToFit); if info <> NoInfo then with info^ do begin ScaleToFitWindow := not ScaleToFitWindow; KillRoi; if ScaleToFitWindow then begin savewrect := wrect; SaveSrcRect := SrcRect; SaveMagnification := magnification; GetWindowRect(wptr, trect); savehloc := trect.left; savevloc := trect.top; wrect := wptr^.PortRect; SrcRect := PicRect; ScaleImageWindow(wrect); SizeWindow(wptr, wrect.right, wrect.bottom, true); end else begin if WindowState = TiledBigScaled then begin wrect := initwrect; SrcRect := wrect; magnification := 1.0; WindowState := NormalWindow; end else begin wrect := savewrect; SrcRect := SaveSrcRect; magnification := SaveMagnification; end; HideWindow(wptr); SizeWindow(wptr, wrect.right, wrect.bottom, true); MoveWindow(wptr, savehloc, savevloc, true); ShowWindow(wptr); ShowMagnification; end; SetPort(wptr); InvalRect(wrect); WindowState := NormalWindow; end; end; procedure DrawMyGrowIcon;{(w:WindowPtr)} var tPort: GrafPtr; tRect: rect; begin GetPort(tPort); SetPort(w); PenNormal; with w^.PortRect do begin SetRect(tRect, right - 12, bottom - 12, right - 5, bottom - 5); FrameRect(tRect); MoveTo(right - 6, bottom - 10); LineTo(right - 2, bottom - 10); LineTo(right - 2, bottom - 2); LineTo(right - 10, bottom - 2); LineTo(right - 10, bottom - 6); end; SetPort(tPort); end; procedure ShowMagnification; {Updates the window title bar to show the current magnification.} var str: str255; begin with info^ do begin if (magnification = 1.0) and not ScaleToFitWindow then str := title else begin if ScaleToFitWindow then begin RealToString(magnification, 1, 2, str); str := concat(title, ' (', str, ')'); end else begin RealToString(magnification, 1, 0, str); str := concat(title, ' (', str, ':1)'); end; end; SetWTitle(wptr, str); end; end; procedure Unzoom; begin if Info <> NoInfo then with Info^ do begin if ScaleToFitWindow then ScaleToFit else begin wrect := initwrect; SrcRect := wrect; end; SizeWindow(wptr, wrect.right, wrect.bottom, true); LoadLUT(info^.cTable); UpdatePicWindow; magnification := 1.0; DrawMyGrowIcon(wptr); ShowMagnification; if WhatToUndo = UndoZoom then WhatToUndo := NothingToUndo; ShowRoi; end; end; function FindMedian;{(VAR a:SortArray):integer} {Finds the 5th largest of 9 values} var i, j, mj, max: integer; begin for i := 1 to 4 do begin max := 0; mj := 1; for j := 1 to 9 do if a[j] > max then begin max := a[j]; mj := j; end; a[mj] := 0; end; max := 0; for j := 1 to 9 do if a[j] > max then max := a[j]; FindMedian := max; end; procedure DrawBString;{(str:string)} begin TextFace([bold]); DrawString(str); TextFace([]); end; procedure PutWarning; var BufSizeStr: str255; begin NumToString(UndoBufSize div 1024, BufSizeStr); PutMessage(concat('This image is larger than the ', BufSizeStr, 'K Undo buffer. Many operations may fail or be Undoable.')); end; procedure SetupRoiRect; {Copies the current image to Undo buffer so it can be used for drawing} {the "marching ants". The copy of the previous image in the Clipboard buffer} { buffer will be used for Undo.} begin SetupUndo; UndoFromClip := true; info^.RoiShowing := true; end; procedure GetForegroundColor;{(event: EventRecord)} var loc: point; color: integer; begin loc := event.where; ScreenToOffScreen(loc); Color := MyGetPixel(loc.h, loc.v); SetForegroundColor(color); end; procedure GetBackgroundColor; {(event: EventRecord)} var loc: point; color: integer; begin loc := event.where; ScreenToOffScreen(loc); Color := MyGetPixel(loc.h, loc.v); SetBackgroundColor(color); end; procedure GenerateValues; var a, b, c, d, e, f, x, y: extended; i: integer; begin with info^ do begin if not calibrated then begin for i := 0 to 255 do cvalue[i] := i; MinValue := 0.0; MaxValue := 255.0; exit(GenerateValues); end; a := Coefficient[1]; b := Coefficient[2]; c := Coefficient[3]; d := Coefficient[4]; e := Coefficient[5]; f := Coefficient[6]; MinValue := 10e+12; MaxValue := -MinValue; for i := 0 to 255 do begin x := i; case fit of StraightLine: y := a + b * x; Poly2: y := a + b * x + c * x * x; Poly3: y := a + b * x + c * x * x + d * x * x * x; Poly4: y := a + b * x + c * x * x + d * x * x * x + e * x * x * x * x; Poly5: y := a + b * x + c * x * x + d * x * x * x + e * x * x * x * x + f * x * x * x * x * x; ExpoFit: y := a * exp(b * x); PowerFit: if x = 0.0 then y := 0.0 else y := a * exp(b * ln(x)); {y=ax^b} LogFit: begin if x = 0.0 then x := 0.000001; y := a * ln(b * x) end; RodbardFit: begin if x <= a then y := 0 else begin y := (a - x) / (x - d); y := exp(ln(y) * (1 / b)); {y:=y**(1/b)} y := y * c; end; end; end; cvalue[i] := y; if y > MaxValue then MaxValue := y; if y < MinValue then MinValue := y; end; end; end; procedure ScaleImageWindow (var trect: rect); var WindowLeft, WindowTop: integer; PicAspectRatio, TempMagnification: extended; begin with info^ do begin SrcRect := PicRect; with CGrafPort(wptr^).PortPixMap^^.bounds do begin WindowLeft := -left; WindowTop := -top; end; with PicRect do PicAspectRatio := right / bottom; with trect do begin if (WindowLeft + right) > (ScreenWidth - 5) then right := ScreenWidth - 5 - WindowLeft; bottom := round(right / PicAspectRatio); if (WindowTop + bottom) > (ScreenHeight - 5) then bottom := ScreenHeight - 5 - WindowTop; right := round(bottom * PicAspectRatio); magnification := right / PicRect.right; end; ShowMagnification; end; {with} end; function TooWide: boolean; var SelectionTooWide: boolean; MaxWidth: str255; begin with info^.RoiRect do SelectionTooWide := (right - left) > MaxPixelsPerLine; if SelectionTooWide then begin NumToString(MaxPixelsPerLine, MaxWidth); PutMessage(concat('This operation does not support selections wider than ', MaxWidth, ' pixels.')); end; TooWide := SelectionTooWide; end; procedure DrawText (str: str255; loc: point; just: integer); var SaveJust: integer; begin TextStr := str; IsInsertionPoint := true; TextStart := loc; SaveJust := TextJust; TextJust := just; DisplayText(false); TextJust := SaveJust; IsInsertionPoint := false; end; procedure IncrementCounter; begin if mCount < MaxRegions then begin mCount := mCount + 1; UnsavedResults := true; end else beep; end; procedure ClearResults (i: integer); begin mean^[i] := 0.0; sd^[i] := 0.0; PixelCount^[i] := 0; mArea^[i] := 0.0; mode^[i] := 0.0; IntegratedDensity^[i] := 0.0; idBackground^[i] := 0.0; xcenter^[i] := 0.0; ycenter^[i] := 0.0; MajorAxis^[i] := 0.0; MinorAxis^[i] := 0.0; orientation^[i] := 0.0; mMin^[i] := 0.0; mMax^[i] := 0.0; plength^[i] := 0.0; end; procedure UpdateFitEllipse; begin FitEllipse := (xyLocM in measurements) or (MajorAxisM in measurements) or (MinorAxisM in measurements) or (AngleM in measurements); end; end.