unit Stacks; interface uses QuickDraw, Palettes, QDOffscreen, PictUtil, PrintTraps, globals, Utilities, Graphics, Analysis, Camera, file1, file2, filters, sound, lut; function MakeStackFromWindow: boolean; procedure MakeStack; procedure MakeWindowsFromStack; function AddSlice (update: boolean): boolean; procedure DeleteSlice; procedure ShowNextSlice (item: integer); procedure ShowFirstOrLastSlice (ich: integer); procedure DoResliceOptions; procedure Reslice; procedure Animate; procedure MakeMovie; procedure CaptureFrames; procedure MakeMontage; procedure ConvertRGBToEightBitColor (Capturing: boolean); procedure ConvertEightBitColorToRGB; procedure CaptureColor; procedure AverageSlices; procedure ConvertRGBToHSV; implementation function MakeStackFromWindow: boolean; begin with info^ do begin StackInfo := StackInfoPtr(NewPtr(SizeOf(StackInfoRec))); if StackInfo = nil then begin MakeStackFromWindow := false; exit(MakeStackFromWindow); end; with StackInfo^ do begin nSlices := 1; CurrentSlice := 1; PicBaseH[1] := PicBaseHandle; SliceSpacing := 0.0; LoopTime := 0.0; end; PictureType := NewPicture; MakeStackFromWindow := true; end; end; procedure MakeStack; var ok, isStack: boolean; i, result: integer; TempInfo, SaveInfo: InfoPtr; str: str255; begin if not AllSameSize then begin PutMessage('All currently open images must be the same size to make a stack.'); exit(MakeStack); end; isStack := false; for i := 1 to nPics do begin TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon); isStack := isStack or (TempInfo^.StackInfo <> nil); end; if isStack then begin PutMessage('All stacks must be closed before making a new stack.'); exit(MakeStack); end; if nPics > MaxSlices then begin NumToString(MaxSlices, str); PutMessage(concat('Maximun stack size is ', str, ' slices.')); exit(MakeStack); end; StopDigitizing; DisableDensitySlice; SelectWindow(PicWindow[1]); Info := pointer(WindowPeek(PicWindow[1])^.RefCon); ActivateWindow; KillRoi; UnZoom; if not MakeStackFromWindow then exit(MakeStack); with info^ do begin StackInfo^.nSlices := nPics; title := 'Stack'; UpdateTitleBar; Revertable := false; end; SaveInfo := Info; MakingStack := true; ShowWatch; for i := 2 to nPics do begin TempInfo := pointer(WindowPeek(PicWindow[2])^.RefCon); with TempInfo^ do begin hunlock(PicBaseHandle); info^.StackInfo^.PicBaseH[i] := PicBaseHandle; end; result := CloseAWindow(PicWindow[2]); Info := SaveInfo; end; with info^ do UpdateWindowsMenuItem(PixMapSize * StackInfo^.nSlices, title, 1); MakingStack := false; end; function AddSlice (update: boolean): boolean; var i: integer; h: handle; isRoi: boolean; begin with info^, info^.StackInfo^ do begin AddSlice := false; if nSlices = MaxSlices then exit(AddSlice); isRoi := RoiShowing; if isRoi then KillRoi; h := GetBigHandle(PixMapSize); if h = nil then begin PutMessage('Not enough memory available to add a slice to this stack.'); macro := false; exit(AddSlice); end; for i := nSlices downto CurrentSlice + 1 do PicBaseH[i + 1] := PicBaseH[i]; nSlices := nSlices + 1; CurrentSlice := CurrentSlice + 1; PicBaseH[CurrentSlice] := h; SelectSlice(CurrentSlice); if Update then begin SelectAll(false); DoOperation(EraseOp); UpdatePicWindow; end; UpdateTitleBar; if isRoi then RestoreRoi; WhatToUndo := NothingToUndo; AddSlice := true; changes := true; PictureType := NewPicture; UpdateWindowsMenuItem(PixMapSize * nSlices, title, PicNum); end; end; procedure DeleteSlice; var SliceToDelete, NextSlice, i: integer; isRoi: boolean; begin with info^, info^.StackInfo^ do begin if nSlices = 1 then begin WhatToUndo := NothingToUndo; exit(DeleteSlice); end; isRoi := RoiShowing; if isRoi then KillRoi; SetupUndo; WhatToUndo := UndoSliceDelete; SliceToDelete := CurrentSlice; if CurrentSlice = 1 then begin NextSlice := 2; WhatToUndo := UndoFirstSliceDelete; end else NextSlice := CurrentSlice - 1; SelectSlice(NextSlice); UpdatePicWindow; DisposHandle(PicBaseH[SliceToDelete]); for i := SliceToDelete to nSlices - 1 do PicBaseH[i] := PicBaseH[i + 1]; nSlices := nSlices - 1; if CurrentSlice <> 1 then CurrentSlice := CurrentSlice - 1; UpdateTitleBar; if isRoi then RestoreRoi; changes := true; UpdateWindowsMenuItem(PixMapSize * nSlices, title, PicNum); end; end; procedure MakeWindowsFromStack; var i, ignore, N: integer; SaveInfo: InfoPtr; tmp: longint; function MakeName (i: integer): str255; var str: str255; begin RealToString(i, 3, 0, str); if str[1] = ' ' then str[1] := '0'; if str[2] = ' ' then str[2] := '0'; MakeName := str; end; begin N := info^.StackInfo^.nSlices; tmp := SizeOf(PicInfo); if MaxBlock < (MinFree + info^.ImageSize + (SizeOf(PicInfo) + 2000) * LongInt(N)) then begin PutMessage('There is not enough memory available to convert this stack to windows.'); exit(MakeWindowsFromStack); end; SaveInfo := Info; KillRoi; for i := 1 to N - 1 do begin SelectSlice(1); info^.StackInfo^.CurrentSlice := 1; if not Duplicate(MakeName(i), false) then exit(MakeWindowsFromStack); info := SaveInfo; DeleteSlice; end; if Duplicate(MakeName(N), false) then begin info := SaveInfo; info^.changes := false; ignore := CloseAWindow(info^.wptr); end; end; procedure ShowNextSlice (item: integer); var isRoi: boolean; begin with info^, info^.StackInfo^ do begin if item = NextSliceItem then begin CurrentSlice := CurrentSlice + 1; if CurrentSlice > nSlices then CurrentSlice := nSlices; end else begin CurrentSlice := CurrentSlice - 1; if CurrentSlice < 1 then CurrentSlice := 1; end; isRoi := RoiShowing; if isRoi then KillRoi; SelectSlice(CurrentSlice); UpdatePicWindow; UpdateTitleBar; WhatToUndo := NothingToUndo; if isRoi then RestoreRoi; end; end; procedure ShowFirstOrLastSlice (ich: integer); var isRoi: boolean; begin with info^, info^.StackInfo^ do begin if ich = EndKey then CurrentSlice := nSlices else CurrentSlice := 1; isRoi := RoiShowing; if isRoi then KillRoi; SelectSlice(CurrentSlice); UpdatePicWindow; UpdateTitleBar; WhatToUndo := NothingToUndo; if isRoi then RestoreRoi; end; end; procedure DoResliceOptions; var default, tmp: extended; Canceled: boolean; prompt: str255; begin with info^.StackInfo^, info^ do begin if SliceSpacing = 0.0 then default := 1.0 else begin if SpatiallyCalibrated then default := SliceSpacing / xSpatialScale else default := SliceSpacing; end; tmp := GetReal(concat('Slice Spacing(', xUnit, '):'), default, Canceled); if not Canceled and (tmp > 0.0) then begin if SpatiallyCalibrated then SliceSpacing := tmp * xSpatialScale else SliceSpacing := tmp; end; end; end; procedure GetSlice (xstart, ystart, start: real; angle: extended; count: integer; var line: LineType); var i: integer; x, y, xinc, yinc: extended; IntegerStart: boolean; begin IntegerStart := (xstart = trunc(xstart)) and (ystart = trunc(ystart)); if IntegerStart and (angle = 0.0) then begin GetLine(trunc(xstart), trunc(ystart), count, line); exit(GetSlice); end; if IntegerStart and (angle = 270.0) then begin GetColumn(trunc(xstart), trunc(ystart), count, line); exit(GetSlice); end; angle := (angle / 180.0) * pi; xinc := cos(angle); yinc := -sin(angle); x := xstart + start * xinc; y := ystart + start * yinc; for i := 0 to count - 1 do begin line[i] := round(GetInterpolatedPixel(x, y)); x := x + xinc; y := y + yinc; end; end; procedure Reslice; var DstWidth, DstHeight, nSlices: integer; dstLeft, dstTop, y, i, j, LineLength: integer; SaveWindowFlag, SaveMacro, HorizontalMode: boolean; SaveHScale, SaveVScale, UncalibratedLineLength, CalibratedLineLength, angle: extended; Stack, Reconstruction: InfoPtr; aLine: LineType; name, str1, str2: str255; MaskRect: rect; x1, y1, x2, y2, ulength, clength: real; procedure MakeRoi (Left, Top, Width, Height: integer); begin with info^ do begin RoiType := RectRoi; SetRect(RoiRect, left, top, left + width, top + height); MakeRegion; SetupUndo; RoiShowing := true; end; end; begin with info^, info^.StackInfo^ do begin if nSlices < 2 then begin PutMessage('Reslicing requires at least 2 slices.'); macro := false; exit(Reslice); end; if not (RoiShowing and (RoiType = LineRoi)) then begin PutMessage('Please make a straight line selection first.'); macro := false; exit(Reslice); end; Stack := info; GetLengthOrPerimeter(ulength, clength); LineLength := round(ulength); if LineLength = 0 then begin PutMessage('Line length cannot be zero.'); macro := false; exit(Reslice); end; if SliceSpacing = 0.0 then DoResliceOptions; GetLoi(x1, y1, x2, y2); if (LAngle = 0.0) or (LAngle = 270.0) then if NotInBounds then exit(Reslice); HorizontalMode := not OptionKeyWasDown; if HorizontalMode then begin DstWidth := LineLength; DstHeight := round(nSlices * SliceSpacing); if DstHeight < nSlices then DstHeight := nSlices; dstLeft := 0; dstTop := round((dstHeight - nSlices) / 2); end else begin DstWidth := round(nSlices * SliceSpacing); if DstWidth < nSlices then DstWidth := nSlices; DstHeight := LineLength; dstLeft := round((dstWidth - nSlices) / 2); dstTop := 0; end; RealToString(y1, 3, 0, str1); RealToString(LAngle, 1, 2, str2); name := concat(str1, '-', str2); if not NewPicWindow(name, DstWidth, DstHeight) then exit(Reslice); Reconstruction := info; SaveWindowFlag := rsCreateNewWindow; SaveHScale := rsHScale; SaveVScale := rsVScale; rsCreateNewWindow := false; rsMethod := bilinear; for i := 1 to nSlices do begin Info := Stack; SelectSlice(i); GetSlice(x1, y1, 0.0, LAngle, LineLength, aLine); info := Reconstruction; if HorizontalMode then begin PutLine(dstLeft, dstTop + nSlices - i, LineLength, aLine); if i = 1 then {Draw extra line needed to get scaling to work right.} PutLine(dstLeft, dstTop + nSlices, LineLength, aLine); SetRect(MaskRect, dstLeft, dstTop + nSlices - i, dstLeft + LineLength, dstTop + nSlices - i + 1); end else begin PutColumn(dstLeft + nSlices - i, dstTop, LineLength, aLine); if i = 1 then {Draw extra line needed to get scaling to work right.} PutLine(dstLeft + nSlices, dstTop, LineLength, aLine); SetRect(MaskRect, dstLeft + nSlices - i, dstTop, dstLeft + nSlices - i + 1, dstTop + LineLength); end; UpdateScreen(MaskRect); end; if HorizontalMode then begin MakeRoi(dstLeft, dstTop, LineLength, nSlices); rsHScale := 1.0; rsVScale := SliceSpacing; end else begin MakeRoi(dstLeft, dstTop, nSlices, LineLength); rsHScale := SliceSpacing; rsVScale := 1.0; end; rsAngle := 0; SaveMacro := macro; macro := true; ScaleAndRotate; macro := SaveMacro; Info := Stack; SelectSlice(CurrentSlice); Info := Reconstruction; rsCreateNewWindow := SaveWindowFlag; rsHScale := SaveHScale; rsVScale := SaveVScale; KillRoi; end; end; procedure Animate; var n, SaveN, fpsInterval, DelayCount: integer; Event: EventRecord; ch: char; b: boolean; SingleStep, GoForward, NewKeyDown, PhotoMode: boolean; nFrames, StartTicks, NextTicks, SaveTicks, ticks: LongInt; fps, seconds: extended; procedure ShowFPS (fps: extended); var hstart, vstart, ivalue: integer; key: str255; begin if PhotoMode then exit(ShowFPS); hstart := InfoHStart; vstart := InfoVStart; SetPort(InfoWindow); MoveTo(xValueLoc, vstart); case DelayTicks of 0: key := '9 '; 2: key := '8 '; 3: key := '7 '; 4: key := '6 '; 6: key := '5 '; 8: key := '4 '; 12: key := '3 '; 30: key := '2 '; 60: key := '1 '; end; if SingleStep then begin if GoForward then key := '->' else key := '<-'; end; DrawString(key); MoveTo(yValueLoc, vstart + 10); DrawReal(fps, 1, 2); DrawChar(' '); end; begin if info^.StackInfo = nil then begin PutMessage('Animation requires a stack.'); exit(Animate); end; with info^, info^.StackInfo^ do begin if nSlices < 2 then begin PutMessage('Animation requires at least two "slices".'); exit(Animate); end; KillRoi; PhotoMode := OptionKeyDown or OptionKeyWasDown; if PhotoMode then EraseScreen else begin ShowWatch; ShowMessage(concat('Use 1...9 keys to control speed', cr, 'Use arrow keys to single step', cr, 'Press mouse button to stop')); end; FlushEvents(EveryEvent, 0); fpsInterval := 10; SaveN := -1; n := 1; GoForward := true; SingleStep := false; nFrames := 0; StartTicks := TickCount; NextTicks := StartTicks; SaveTicks := StartTicks; if not PhotoMode then begin DrawLabels('key:', 'fps:', ''); SetPort(InfoWindow); TextSize(9); TextFont(Monaco); TextMode(SrcCopy); end; repeat b := WaitNextEvent(EveryEvent, Event, 0, nil); NewKeyDown := (event.what = KeyDown) or (event.what = AutoKey); if NewKeyDown then begin Ch := chr(BitAnd(Event.message, 127)); SingleStep := false; case ord(ch) of 28, 44, 60, PageUp: {<-, <} begin SingleStep := true; GoForward := false; n := n - 1; if n < 1 then n := 1; DelayTicks := 0 end; {left} 29, 46, 62, PageDown: {->, >} begin SingleStep := true; GoForward := true; n := n + 1; if n > nSlices then n := nSlices; DelayTicks := 0 end; {right} 57: DelayTicks := 0; {'9'-max speed} 56: DelayTicks := 2; {'8'-30 fps} 55: DelayTicks := 3; {'7'-20 fps} 54: DelayTicks := 4; {'6'-15 fps} 53: DelayTicks := 6; {'5'-10 fps} 52: DelayTicks := 8; {'4'-7.5 fps} 51: DelayTicks := 12; {'3'-5 fps} 50: DelayTicks := 30; {'2'-2 fps} 49: DelayTicks := 60; {'1'-1 fps} otherwise end; {case} if DelayTicks > 12 then fpsInterval := 2 else if DelayTicks > 3 then fpsInterval := 5 else fpsInterval := 10; end; {if NewKeyDown} if GoForward then begin if not SingleStep then n := n + 1; if n > nSlices then begin if OscillatingMovies then begin n := nSlices - 1; GoForward := false; end else n := 1; end; end else begin if not SingleStep then n := n - 1; if n < 1 then begin if OscillatingMovies then begin n := 2; Goforward := true; end else n := nSlices; end; end; CurrentSlice := n; SelectSlice(CurrentSlice); UpdatePicWindow; nFrames := nFrames + 1; if SingleStep then begin if (not OptionKeyWasDown) and (n <> SaveN) then begin UpdateTitleBar; SaveN := n; end; ShowFPS(0.0); end else if (nFrames mod fpsInterval) = 0 then begin ticks := TickCount; seconds := (ticks - SaveTicks) / 60.0; if seconds <> 0.0 then fps := fpsInterval / seconds else fps := 0.0; ShowFPS(fps); SaveTicks := ticks; end; DelayCount := 0; if DelayTicks > 0 then begin repeat ticks := TickCount; until ticks >= NextTicks; NextTicks := ticks + DelayTicks; end; until (event.what = MouseDown) or (event.what = osEvt); if PhotoMode then RestoreScreen; FlushEvents(EveryEvent, 0); end; {with} end; procedure MakeMovie; var nFrames, wleft, wtop, width, height, frame, i: integer; ignore, SaveFW: integer; OutOfMemory: boolean; DisplayPoint: point; StartTicks, NextTicks, interval, ElapsedTime: LongInt; SecondsBetweenFrames, seconds: extended; frect: rect; MainDevice: GDHandle; SourcePixMap: PixMapHandle; str1, str2, str3: str255; Canceled: boolean; begin with info^ do begin if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin PutMessage('You must be capturing to make a movie.'); exit(MakeMovie); end; StopDigitizing; if not (RoiShowing and (RoiType = RectRoi)) then begin PutMessage('Please make a rectangular selection first.'); exit(MakeMovie); end; if NotInBounds then exit(MakeMovie); SaveFW := FramesWanted; FramesWanted := GetInt('Number of Frames:', FramesWanted, Canceled); if Canceled then begin FramesWanted := SaveFW; exit(MakeMovie); end; if FramesWanted < 1 then FramesWanted := 1; if FramesWanted > MaxSlices then FramesWanted := MaxSlices; with RoiRect do begin left := band(left + 1, $fffc); {Word align} right := band(right + 2, $fffc); if right > PicRect.right then right := PicRect.right; MakeRegion; wleft := left; wtop := top; width := right - left; height := bottom - top; end; end; {with info^} if FrameGrabber = Scion then begin with DisplayPoint do begin h := PicLeftBase; v := PicTopBase; end; with frect do begin left := PicLeftBase + wleft; top := PicTopBase + wtop; right := left + width; bottom := top + height; end; end else with frect do begin left := wleft; top := wtop; right := left + width; bottom := top + height; end; if not NewPicWindow('Movie', width, height) then exit(MakeMovie); if not MakeStackFromWindow then exit(MakeMovie); nFrames := 1; OutOfMemory := false; while (nFrames < FramesWanted) and (not OutOfMemory) do begin OutOfMemory := not AddSlice(false); if not OutOfMemory then nFrames := nFrames + 1; end; if ExternalTrigger then SecondsBetweenFrames := 0.0 else SecondsBetweenFrames := GetReal('Delay Between Frames(seconds):', 0.0, Canceled); if Canceled then with info^ do begin changes := false; ignore := CloseAWindow(wptr); Exit(MakeMovie); end; if SecondsBetweenFrames < 0.0 then SecondsBetweenFrames := 0.0; interval := round(60.0 * SecondsBetweenFrames); if FrameGrabber = Scion then begin HideCursor; MainDevice := GetMainDevice; SourcePixMap := MainDevice^^.gdPMap; end else begin ShowWatch; SourcePixMap := fgPort^.portPixMap; ResetFrameGrabber; end; ShowTriggerMessage; StartTicks := TickCount; NextTicks := StartTicks; with info^, info^.StackInfo^ do begin if Interval >= 30 then ShowMessage(CmdPeriodToStop) else DrawLabels('Frame:', 'Total:', ''); for frame := 1 to nFrames do begin CurrentSlice := frame; SelectSlice(CurrentSlice); NextTicks := NextTicks + Interval; if FrameGrabber = Scion then begin GetScionFrame(DisplayPoint); CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect); end else begin if Interval >= 30 then UpdateTitleBar else Show2Values(CurrentSlice, nSlices); GetFrame; CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect); if not BlindMovieCapture then UpdatePicWindow; end; while TickCount < NextTicks do if CommandPeriod then begin beep; wait(60); exit(MakeMovie); end; end; {for} seconds := (TickCount - StartTicks) / 60.0; LoopTime := seconds; end; {with} RealToString(seconds, 1, 2, str1); str1 := concat(long2str(nFrames), ' frames', cr, str1, ' seconds', cr); RealToString(seconds / nFrames, 1, 3, str2); str3 := concat(str1, str2, ' seconds/frame', cr); if nFrames >= seconds then ShowFrameRate(str3, StartTicks, nFrames) else ShowMessage(str3); ShowFirstOrLastSlice(HomeKey); end; procedure CaptureFrames; var nFrames, wleft, wtop, width, height, i: integer; ignore, SaveFW: integer; OutOfMemory, AdvanceFrame, b: boolean; DisplayPoint: point; frect: rect; MainDevice: GDHandle; SourcePixMap: PixMapHandle; Event: EventRecord; ShutterSound: handle; err: OSErr; procedure CheckButton; begin if Button and not AdvanceFrame then with Info^.StackInfo^ do begin AdvanceFrame := true; ShutterSound := GetResource('snd ', 100); if ShutterSound <> nil then err := SndPlay(nil, ShutterSound, false); if CurrentSlice < nSlices then begin CurrentSlice := CurrentSlice + 1; UpdateTitleBar; CurrentSlice := CurrentSlice - 1; end; end; end; begin with info^ do begin if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin PutMessage('You must be capturing to capture frames.'); exit(CaptureFrames); end; StopDigitizing; if not (RoiShowing and (RoiType = RectRoi)) then begin PutMessage('Please make a rectangular selection first.'); exit(CaptureFrames); end; if NotInBounds then exit(CaptureFrames); SaveFW := FramesWanted; ShutterSound := nil; with RoiRect do begin left := band(left + 1, $fffc); {Word align} right := band(right + 2, $fffc); if right > PicRect.right then right := PicRect.right; MakeRegion; wleft := left; wtop := top; width := right - left; height := bottom - top; end; end; {with info^} if FrameGrabber = Scion then begin with DisplayPoint do begin h := PicLeftBase; v := PicTopBase; end; with frect do begin left := PicLeftBase + wleft; top := PicTopBase + wtop; right := left + width; bottom := top + height; end; end else with frect do begin left := wleft; top := wtop; right := left + width; bottom := top + height; end; if not NewPicWindow('Frames', width, height) then exit(CaptureFrames); if not MakeStackFromWindow then exit(CaptureFrames); UpdateTitleBar; if FrameGrabber = Scion then begin HideCursor; MainDevice := GetMainDevice; SourcePixMap := MainDevice^^.gdPMap; end else begin ShowWatch; SourcePixMap := fgPort^.portPixMap; ResetFrameGrabber; end; FlushEvents(EveryEvent, 0); ExternalTrigger := false; UpdateVideoControl; with info^, info^.StackInfo^ do begin ShowMessage(CmdPeriodToStop); OutOfMemory := false; AdvanceFrame := false; while (not OutOfMemory) and (CurrentSlice <= MaxSlices) do begin if AdvanceFrame then begin OutOfMemory := not AddSlice(false); AdvanceFrame := false; end; if FrameGrabber = Scion then begin GetScionFrame(DisplayPoint); CheckButton; CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect); CheckButton; end else begin GetFrame; CheckButton; CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect); CheckButton; UpdatePicWindow; CheckButton; end; b := WaitNextEvent(EveryEvent, Event, 0, nil); if event.what = KeyDown then leave; end; {while} end; {with} if ShutterSound <> nil then ReleaseResource(ShutterSound); end; procedure CopyPics (sPort, dPort: cGrafPtr; sRect, dRect: rect); begin pmForeColor(BlackIndex); pmBackColor(WhiteIndex); hlock(handle(sPort^.portPixMap)); hlock(handle(dPort^.portPixMap)); CopyBits(BitMapHandle(sPort^.portPixMap)^^, BitMapHandle(dPort^.PortPixMap)^^, sRect, dRect, SrcCopy, nil); hunlock(handle(sPort^.portPixMap)); hunlock(handle(dPort^.PortPixMap)); pmForeColor(ForegroundIndex); pmBackColor(BackgroundIndex); end; procedure MakeMontage; {Opens a new window and creates a composite image} {from the slices in the current stack.} const ColumnsID = 3; RowsID = 4; ScaleID = 5; FirstID = 6; LastID = 7; IncrementID = 8; NumberID = 9; var mylog: DialogPtr; item, i, nRows, nColumns, Inc, slices: integer; StackWidth, StackHeight, mWidth, mHeight, Background: integer; dWidth, dHeight, dLeft, dTop, dRight, dBottom, MaxWidth, MaxHeight: integer; FirstSlice, LastSlice, TotalSlices: integer; scale, SaveScale: extended; sPort, dPort: cGrafPtr; StackInfo, MontageInfo: InfoPtr; sRect, dRect: rect; NumberSlices, IncrementSet: boolean; str: str255; loc: point; SaveGDevice: GDHandle; procedure Estimate (adjustinc: boolean); var tmp, xScale, yScale: extended; n: integer; begin slices := LastSlice - FirstSlice + 1; if adjustinc then inc := 0; repeat if adjustinc then inc := inc + 1; n := trunc(slices / inc); tmp := sqrt(n); if trunc(tmp) <> tmp then tmp := trunc(tmp) + 1.0; nColumns := trunc(tmp); nRows := nColumns; if (nColumns * (nRows - 1)) >= n then nRows := nRows - 1; xScale := (MaxWidth / nColumns) / StackWidth; yScale := (MaxHeight / nRows) / StackHeight; if xScale < yScale then scale := xScale else scale := yScale; if scale > 1.0 then scale := 1.0; SaveScale := scale; until (scale >= 0.5) or (inc >= 3) or not adjustinc; end; begin InitCursor; with info^ do begin StackWidth := PixelsPerLine; StackHeight := nLines; FirstSlice := 1; TotalSlices := StackInfo^.nSlices; LastSlice := TotalSlices; end; MaxWidth := ScreenWidth - 85; MaxHeight := ScreenHeight - 45; Estimate(true); NumberSlices := true; IncrementSet := false; mylog := GetNewDialog(150, nil, pointer(-1)); SetDNum(MyLog, RowsID, nRows); SetDNum(MyLog, ColumnsID, nColumns); SetDReal(MyLog, ScaleID, scale, 2); SetDNum(MyLog, FirstID, FirstSlice); SetDNum(MyLog, LastID, LastSlice); SetDNum(MyLog, IncrementID, inc); SetDialogItem(MyLog, NumberID, ord(NumberSlices)); OutlineButton(MyLog, ok, 16); repeat ModalDialog(nil, item); if item = ColumnsID then begin nColumns := GetDNum(MyLog, ColumnsID); if nColumns < 0 then begin nColumns := 0; SetDNum(MyLog, ColumnsID, nRows); end; end; if item = RowsID then begin nRows := GetDNum(MyLog, RowsID); if nRows < 0 then begin nRows := 0; SetDNum(MyLog, RowsID, nRows); end; end; if item = ScaleID then scale := GetDReal(MyLog, ScaleID); if item = FirstID then begin FirstSlice := GetDNum(MyLog, FirstID); if (FirstSlice < 1) or (FirstSlice > LastSlice) then FirstSlice := 1; if IncrementSet then Estimate(false) else Estimate(true); SetDNum(MyLog, RowsID, nRows); SetDNum(MyLog, ColumnsID, nColumns); SetDReal(MyLog, ScaleID, scale, 2); end; if item = LastID then begin LastSlice := GetDNum(MyLog, LastID); if (LastSlice < FirstSlice) or (LastSlice > TotalSlices) then LastSlice := TotalSlices; if IncrementSet then Estimate(false) else Estimate(true); SetDNum(MyLog, RowsID, nRows); SetDNum(MyLog, ColumnsID, nColumns); SetDReal(MyLog, ScaleID, scale, 2); end; if item = IncrementID then begin inc := GetDNum(MyLog, IncrementID); IncrementSet := true; if (inc < 1) or (inc > (slices div 2)) then begin inc := 1; SetDNum(MyLog, IncrementID, inc); end; Estimate(false); SetDNum(MyLog, RowsID, nRows); SetDNum(MyLog, ColumnsID, nColumns); SetDReal(MyLog, ScaleID, scale, 2); end; if item = NumberID then begin NumberSlices := not NumberSlices; SetDialogItem(MyLog, NumberID, ord(NumberSlices)); end; until (item = ok) or (item = cancel); DisposDialog(mylog); if item = cancel then exit(MakeMontage); if (scale <= 0.05) or (scale > 5) then scale := SaveScale; dWidth := round(StackWidth * scale); dHeight := round(StackHeight * scale); mWidth := nColumns * dWidth; mHeight := nRows * dHeight; StackInfo := info; Background := MyGetPixel(0, 0); SetBackgroundColor(Background); if Background = WhiteIndex then SetForegroundColor(BlackIndex) else SetForegroundColor(WhiteIndex); if not NewPicWindow('Montage', mWidth, mHeight) then exit(MakeMontage); MontageInfo := info; SaveGDevice := GetGDevice; SetGDevice(osGDevice); if NumberSlices then begin SetPort(GrafPtr(info^.osPort)); pmForeColor(ForegroundIndex); TextFont(ApplFont); TextSize(9); end; dPort := info^.osPort; dLeft := 0; dTop := 0; sPort := StackInfo^.osPort; sRect := StackInfo^.PicRect; i := FirstSlice; while i <= LastSlice do begin Info := StackInfo; SelectSlice(i); SetRect(dRect, dLeft, dTop, dLeft + dWidth, dTop + DHeight); CopyPics(sPort, dPort, sRect, dRect); info := MontageInfo; if NumberSlices then begin MoveTo(dLeft + (dWidth div 2) - 3, dTop + dHeight - 9); NumToString(i, str); loc.h := dLeft + (dWidth div 2) - 3; loc.v := dTop + dHeight - 5; DrawTextString(str, loc, TeJustCenter); end; UpdateScreen(dRect); dLeft := dLeft + dWidth; if (dLeft + dWidth) > mWidth then begin dLeft := 0; dTop := dTop + dHeight; end; i := i + inc; end; SetGDevice(SaveGDevice); info := StackInfo; SelectSlice(info^.StackInfo^.CurrentSlice); if MontageInfo^.PixMapSize > UndoBufSize then PutWarning; end; procedure CopyRGBToPixMap (pmap: PixMapHandle); type LongPtr = ^LongInt; var row, i, width, WatchRate: integer; RedLine, GreenLine, BlueLine: LineType; Pixel, RowOffset: LongInt; pmapPtr: ptr; LPtr, RowStart: LongPtr; begin with info^ do begin pmapPtr := GetPixBaseAddr(pmap); if pmapPtr = nil then exit(CopyRGBToPixMap); LPtr := LongPtr(pmapPtr); RowStart := LPtr; RowOffset := band(pmap^^.RowBytes, $3FFF); width := PicRect.right; WatchRate := 20000 div PixelsPerLine; for row := 0 to nLines - 1 do begin if (row mod WatchRate) = 0 then ShowAnimatedWatch; SelectSlice(1); GetLine(0, row, width, RedLine); SelectSlice(2); GetLine(0, row, width, GreenLine); SelectSlice(3); GetLine(0, row, width, BlueLine); LPtr := RowStart; for i := 0 to PixelsPerLine - 1 do begin pixel := -1; pixel := RedLine[i]; pixel := bor(bsl(pixel, 8), GreenLine[i]); pixel := bor(bsl(pixel, 8), blueLine[i]); LPtr^ := BitNot(pixel); LPtr := LongPtr(ord4(LPtr) + 4); end; RowStart := LongPtr(ord4(RowStart) + RowOffset); end; SelectSlice(StackInfo^.CurrentSlice); end; {with} end; function DoColorOptions: boolean; const ExistingID = 4; SystemID = 5; CustomID = 6; DitherID = 7; var mylog: DialogPtr; item: integer; procedure UpdateButtons; begin SetDialogItem(mylog, ExistingID, ord(RGBLut = ExistingLUT)); SetDialogItem(mylog, SystemID, ord(RGBLut = SystemLUT)); SetDialogItem(mylog, CustomID, ord(RGBLut = CustomLUT)); end; begin InitCursor; mylog := GetNewDialog(160, nil, pointer(-1)); SetDialogItem(mylog, DitherID, ord(DitherColor)); UpdateButtons; OutlineButton(MyLog, ok, 16); repeat ModalDialog(nil, item); if item = DitherID then begin DitherColor := not DitherColor; SetDialogItem(mylog, DitherID, ord(DitherColor)); end; if item = ExistingID then begin RGBLut := ExistingLUT; UpdateButtons end; if item = SystemID then begin RGBLut := SystemLUT; UpdateButtons; DitherColor := true; SetDialogItem(mylog, DitherID, ord(DitherColor)); end; if item = CustomID then begin RGBLut := CustomLUT; UpdateButtons end; until (item = ok) or (item = cancel); DisposDialog(mylog); DoColorOptions := item <> cancel; end; function Activate (name: str255): boolean; {Activates the window with the specified name.} var i: integer; TempInfo: InfoPtr; begin Activate := false; for i := 1 to nPics do begin TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon); if TempInfo^.title = name then begin if PicWindow[i] <> nil then begin SelectWindow(PicWindow[i]); Info := TempInfo; ActivateWindow; Activate := true; end; {if} leave; end; {if} end; {for} end; procedure ConvertRGBToEightBitColor (Capturing: boolean); var err: QDErr; err2: OSErr; osGWorld: GWorldPtr; flags: GWorldFlags; pmap: PixMapHandle; pRect: rect; thePictInfo: PictInfo; CopyMode, SamplingMethod: integer; UpdateNeeded: boolean; SaveGDevice: GDHandle; procedure abort; begin DisposeGWorld(osGWorld); exit(ConvertRGBToEightBitColor); end; begin if not System7 then begin PutMessage('You must be running System 7 to do 24 to 8-bit color conversions.'); exit(ConvertRGBToEightBitColor); end; with info^ do begin if StackInfo^.nSlices <> 3 then begin PutMessage('24 to 8-bit color conversion requires a three slice(red, green and blue) stack as input.'); exit(ConvertRGBToEightBitColor); end; if Capturing then begin DitherColor := true; RGBLut := CustomLUT; end else if not macro then begin if not DoColorOptions then exit(ConvertRGBToEightBitColor); end; flags := []; err := NewGWorld(osGWorld, 32, PicRect, nil, nil, flags); if err <> NoErr then begin PutMemoryAlert; exit(ConvertRGBToEightBitColor); end; pmap := GetGWorldPixMap(osGWorld); if not LockPixels(pmap) then abort; CopyRGBToPixMap(pmap); pRect := PicRect; end; {with} UpdateNeeded := true; if Activate('Indexed Color') then begin if (info^.PixelsPerLine <> pRect.right) or (info^.nLines <> pRect.bottom) then begin if not NewPicWindow('Indexed Color', pRect.right, pRect.bottom) then abort; UpdateNeeded := false; end end else begin if not NewPicWindow('Indexed Color', pRect.right, pRect.bottom) then abort; UpdateNeeded := false; end; if RGBLut = SystemLUT then SwitchColorTables(SystemPaletteItem, false) else if RGBLut = CustomLut then begin if OptionKeyWasDown then SamplingMethod := PopularMethod else SamplingMethod := SystemMethod; err2 := GetPixMapInfo(pmap, thePictInfo, ReturnColorTable, 256, SamplingMethod, 0); LoadColorTable(thePictInfo.theColorTable); end; SetForegroundColor(BlackIndex); SetBackgroundColor(WhiteIndex); if DitherColor then CopyMode := DitherCopy else CopyMode := SrcCopy; SaveGDevice := GetGDevice; SetGDevice(osGDevice); SetPort(GrafPtr(Info^.osPort)); CopyBits(BitMapHandle(pmap)^^, BitMapHandle(info^.osPort^.PortPixMap)^^, pRect, pRect, CopyMode, nil); SetGDevice(SaveGDevice); DisposeGWorld(osGWorld); if UpdateNeeded then UpdatePicWindow; end; function MakeRGBStack (name: str255): boolean; var ignore: integer; begin MakeRGBStack := false; if not Duplicate(name, false) then exit(MakeRGBStack); if not MakeStackFromWindow then exit(MakeRGBStack); if not AddSlice(false) then begin info^.changes := false; ignore := CloseAWindow(info^.wptr); exit(MakeRGBStack); end; if not AddSlice(false) then begin info^.changes := false; ignore := CloseAWindow(info^.wptr); exit(MakeRGBStack); end; MakeRGBStack := true; end; procedure ConvertEightBitColorToRGB; var width, height, i, row: integer; srcLine, rLine, gLine, bLine: LineType; rLut, gLUT, bLUT: packed array[0..255] of byte; value: byte; begin if isGrayscaleLUT then begin PutMessage('8-bit color to RGB conversion requires a color image.'); exit(ConvertEightBitColorToRGB); end; KillRoi; if not MakeRGBStack(concat(info^.title, '(RGB)')) then exit(ConvertEightBitColorToRGB); LoadLUT(Info^.cTable); for i := 0 to 255 do with cScreenPort^.portPixMap^^.pmTable^^.ctTable[i].rgb do begin rLUT[i] := BitNot(band(bsr(red, 8), 255)); gLUT[i] := BitNot(band(bsr(green, 8), 255)); bLUT[i] := BitNot(band(bsr(blue, 8), 255)); end; width := info^.PixelsPerLine; height := info^.nLines; for row := 0 to height - 1 do begin SelectSlice(1); GetLine(0, row, width, srcLine); for i := 0 to width - 1 do begin value := srcLine[i]; rLine[i] := rLUT[value]; gLine[i] := gLUT[value]; bLine[i] := bLUT[value]; end; PutLine(0, row, width, rLine); SelectSlice(2); PutLine(0, row, width, gLine); SelectSlice(3); PutLine(0, row, width, bLine); end; with Info^.StackInfo^ do begin CurrentSlice := 1; SelectSlice(CurrentSlice); UpdateTitleBar; end; end; procedure CaptureColor; var MainDevice: GDHandle; SourcePixMap: PixMapHandle; frame, width, height, SaveChannel: integer; frect: rect; DisplayPoint: point; begin with info^ do if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin PutMessage('You must be capturing to capture color.'); macro := false; exit(CaptureColor); end; StopDigitizing; with info^.PicRect do begin width := right - left; height := bottom - top; end; if Activate('RGB') then with info^.PicRect do begin if ((right - left) <> width) or ((bottom - top) <> height) then if not MakeRGBStack('RGB') then exit(CaptureColor); end else if not MakeRGBStack('RGB') then exit(CaptureColor); if FrameGrabber = Scion then begin HideCursor; MainDevice := GetMainDevice; SourcePixMap := MainDevice^^.gdPMap; end else begin ShowWatch; SourcePixMap := fgPort^.portPixMap; ResetFrameGrabber; end; if FrameGrabber = Scion then begin with DisplayPoint do begin h := PicLeftBase; v := PicTopBase; end; with frect do begin left := PicLeftBase; top := PicTopBase; right := left + width; bottom := top + height; end; end else with frect do begin left := 0; top := 0; right := left + width; bottom := top + height; end; ShowTriggerMessage; SaveChannel := VideoChannel; with info^, info^.StackInfo^ do begin for frame := 1 to 3 do begin if FrameGrabber = QuickCapture then begin case frame of 1: VideoChannel := 1; {Green} 2: VideoChannel := 0; {Red} 3: VideoChannel := 2; {Blue} end; ResetFrameGrabber; repeat until band(ControlReg^, $8) = 0; {mux channel not busy} end else begin VideoChannel := frame - 1; ResetFrameGrabber; end; if VideoControl <> nil then ShowChannel; CurrentSlice := frame; SelectSlice(CurrentSlice); if FrameGrabber = Scion then begin GetScionFrame(DisplayPoint); CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect); end else begin GetFrame; CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect); end; end; {for} CurrentSlice := 1; SelectSlice(CurrentSlice); UpdateTitleBar; end; {with} VideoChannel := SaveChannel; if VideoControl <> nil then ShowChannel; ConvertRGBToEightBitColor(true); end; procedure AverageSlices; const MaxWidth = 2048; var slices, sRow, aRow, slice, i, SaveSlice: integer; width, height, hstart, vStart: integer; OldInfo, NewInfo: InfoPtr; aLine: LineType; mask: rect; sum: array[0..MaxWidth] of LongInt; AutoSelectAll: boolean; begin OldInfo := Info; with info^ do begin if StackInfo = nil then begin PutMessage('Average Slices requires a stack.'); macro := false; exit(AverageSlices); end; AutoSelectAll := not Info^.RoiShowing; if AutoSelectAll then SelectAll(true); with RoiRect do begin hStart := left; vStart := top; width := right - left; height := bottom - top; end; if width > MaxWidth then begin PutMessage(concat('NIH Image can''t average selections wider than ', Long2str(MaxWidth), ' pixels.')); macro := false; exit(AverageSlices); end; with StackInfo^ do begin slices := StackInfo^.nSlices; SaveSlice := CurrentSlice; end; if not NewPicWindow('Average', width, height) then begin macro := false; exit(AverageSlices); end; end; info^.changes := true; NewInfo := Info; aRow := 0; for sRow := vStart to vStart + height - 1 do begin info := OldInfo; for i := 0 to width - 1 do sum[i] := 0; for slice := 1 to slices do begin SelectSlice(slice); GetLine(hStart, sRow, width, aLine); for i := 0 to width - 1 do sum[i] := sum[i] + aLine[i]; end; for i := 0 to width - 1 do aLine[i] := sum[i] div slices; info := NewInfo; PutLine(0, aRow, width, aLine); SetRect(mask, 0, aRow, width, aRow + 1); aRow := aRow + 1; UpdateScreen(mask); if CommandPeriod then leave; end; info := OldInfo; SelectSlice(SaveSlice); if AutoSelectAll then KillRoi; end; procedure ConvertRGBToHSV; const MaxSaturation = 255; MaxValue = 255; var width, height, i, row, mark: integer; rLine, gLine, bLine, hLine, sLine, vLine: LineType; delta, min, max, R, G, B, H, S, V: integer; tmp: longint; UpdateR: rect; function Max3 (a, b, c: integer): integer; var TempMax: integer; begin if (a > b) then TempMax := a else TempMax := b; if (TempMax > c) then Max3 := TempMax else Max3 := c; end; function Min3 (a, b, c: integer): integer; var TempMin: integer; begin if (a < b) then TempMin := a else TempMin := b; if (TempMin < c) then Min3 := TempMin else Min3 := c; end; begin with info^ do begin if StackInfo^.nSlices <> 3 then begin PutMessage('RGB to HSV color conversion requires a three slice(red, green and blue) stack as input.'); exit(ConvertRGBToHSV); end; if Changes then begin if PutMessageWithCancel('RGB to HSV color conversion is undoable.') = cancel then exit(ConvertRGBToHSV); end; KillRoi; with StackInfo^ do begin CurrentSlice := 1; SelectSlice(CurrentSlice); UpdatePicWindow; end; SwitchColorTables(SpectrumItem, true); title := 'HSV'; UpdateTitleBar; width := PixelsPerLine; height := nLines; mark := 0; ShowWatch; for row := 0 to height - 1 do begin SelectSlice(1); GetLine(0, row, width, rLine); SelectSlice(2); GetLine(0, row, width, gLine); SelectSlice(3); GetLine(0, row, width, bLine); for i := 0 to width - 1 do begin R := 255 - rLine[i]; G := 255 - gLine[i]; B := 255 - bLine[i]; max := Max3(R, G, B); min := Min3(R, G, B); V := max; if max <> 0 then begin tmp := 255 * (max - min); S := (tmp + (tmp mod max)) div max; {adding '(tmp mod max)' simulate rounding} end else S := 0; if S = 0 then H := 0 {undefined but, but select red } else begin delta := max - min; if R = max then begin tmp := 85 * (G - B); H := tmp div delta; end else if G = max then begin tmp := 85 * (B - R); H := 170 + tmp div delta; end else if B = max then begin tmp := 85 * (R - G); H := 340 + tmp div delta; end; H := H div 2; if H < 0 then H := H + 255 end; if H = 0 then hLine[i] := 1 else hLine[i] := H; sLine[i] := S; vLine[i] := 255 - V; end; SelectSlice(1); PutLine(0, row, width, hLine); if (row mod 10) = 0 then begin setrect(UpdateR, 0, mark, width - 1, row); mark := row; UpdateScreen(UpdateR); end; SelectSlice(2); PutLine(0, row, width, sLine); SelectSlice(3); PutLine(0, row, width, vLine); end; SelectSlice(1); end; {with} WhatToUndo := NothingToUndo; end; end.