home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Applications / NIH Image 1.55 / Source / Stacks.p < prev    next >
Encoding:
Text File  |  1994-05-02  |  44.6 KB  |  1,714 lines  |  [TEXT/PJMM]

  1. unit Stacks;
  2.  
  3. interface
  4.  
  5.     uses
  6.         QuickDraw, Palettes, QDOffscreen, PictUtil, PrintTraps, globals, Utilities, Graphics, Analysis, Camera, file1, file2, filters, sound, lut;
  7.  
  8.     function MakeStackFromWindow: boolean;
  9.     procedure MakeStack;
  10.     procedure MakeWindowsFromStack;
  11.     function AddSlice (update: boolean): boolean;
  12.     procedure DeleteSlice;
  13.     procedure ShowNextSlice (item: integer);
  14.     procedure ShowFirstOrLastSlice (ich: integer);
  15.     procedure DoResliceOptions;
  16.     procedure Reslice;
  17.     procedure Animate;
  18.     procedure MakeMovie;
  19.     procedure CaptureFrames;
  20.     procedure MakeMontage;
  21.     procedure ConvertRGBToEightBitColor (Capturing: boolean);
  22.     procedure ConvertEightBitColorToRGB;
  23.     procedure CaptureColor;
  24.     procedure AverageSlices;
  25.     procedure ConvertRGBToHSV;
  26.  
  27.  
  28. implementation
  29.  
  30.  
  31.     function MakeStackFromWindow: boolean;
  32.     begin
  33.         with info^ do begin
  34.                 StackInfo := StackInfoPtr(NewPtr(SizeOf(StackInfoRec)));
  35.                 if StackInfo = nil then begin
  36.                         MakeStackFromWindow := false;
  37.                         exit(MakeStackFromWindow);
  38.                     end;
  39.                 with StackInfo^ do begin
  40.                         nSlices := 1;
  41.                         CurrentSlice := 1;
  42.                         PicBaseH[1] := PicBaseHandle;
  43.                         SliceSpacing := 0.0;
  44.                         LoopTime := 0.0;
  45.                     end;
  46.                 PictureType := NewPicture;
  47.                 MakeStackFromWindow := true;
  48.             end;
  49.     end;
  50.  
  51.  
  52.     procedure MakeStack;
  53.         var
  54.             ok, isStack: boolean;
  55.             i, result: integer;
  56.             TempInfo, SaveInfo: InfoPtr;
  57.             str: str255;
  58.     begin
  59.         if not AllSameSize then begin
  60.                 PutMessage('All currently open images must be the same size to make a stack.');
  61.                 exit(MakeStack);
  62.             end;
  63.         isStack := false;
  64.         for i := 1 to nPics do begin
  65.                 TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
  66.                 isStack := isStack or (TempInfo^.StackInfo <> nil);
  67.             end;
  68.         if isStack then begin
  69.                 PutMessage('All stacks must be closed before making a new stack.');
  70.                 exit(MakeStack);
  71.             end;
  72.         if nPics > MaxSlices then begin
  73.                 NumToString(MaxSlices, str);
  74.                 PutMessage(concat('Maximun stack size is ', str, ' slices.'));
  75.                 exit(MakeStack);
  76.             end;
  77.         StopDigitizing;
  78.         DisableDensitySlice;
  79.         SelectWindow(PicWindow[1]);
  80.         Info := pointer(WindowPeek(PicWindow[1])^.RefCon);
  81.         ActivateWindow;
  82.         KillRoi;
  83.         UnZoom;
  84.         if not MakeStackFromWindow then
  85.             exit(MakeStack);
  86.         with info^ do begin
  87.                 StackInfo^.nSlices := nPics;
  88.                 title := 'Stack';
  89.                 UpdateTitleBar;
  90.                 Revertable := false;
  91.             end;
  92.         SaveInfo := Info;
  93.         MakingStack := true;
  94.         ShowWatch;
  95.         for i := 2 to nPics do begin
  96.                 TempInfo := pointer(WindowPeek(PicWindow[2])^.RefCon);
  97.                 with TempInfo^ do begin
  98.                         hunlock(PicBaseHandle);
  99.                         info^.StackInfo^.PicBaseH[i] := PicBaseHandle;
  100.                     end;
  101.                 result := CloseAWindow(PicWindow[2]);
  102.                 Info := SaveInfo;
  103.             end;
  104.         with info^ do
  105.             UpdateWindowsMenuItem(PixMapSize * StackInfo^.nSlices, title, 1);
  106.         MakingStack := false;
  107.     end;
  108.  
  109.  
  110.     function AddSlice (update: boolean): boolean;
  111.         var
  112.             i: integer;
  113.             h: handle;
  114.             isRoi: boolean;
  115.     begin
  116.         with info^, info^.StackInfo^ do begin
  117.                 AddSlice := false;
  118.                 if nSlices = MaxSlices then
  119.                     exit(AddSlice);
  120.                 isRoi := RoiShowing;
  121.                 if isRoi then
  122.                     KillRoi;
  123.                 h := GetBigHandle(PixMapSize);
  124.                 if h = nil then begin
  125.                         PutMessage('Not enough memory available to add a slice to this stack.');
  126.                         macro := false;
  127.                         exit(AddSlice);
  128.                     end;
  129.                 for i := nSlices downto CurrentSlice + 1 do
  130.                     PicBaseH[i + 1] := PicBaseH[i];
  131.                 nSlices := nSlices + 1;
  132.                 CurrentSlice := CurrentSlice + 1;
  133.                 PicBaseH[CurrentSlice] := h;
  134.                 SelectSlice(CurrentSlice);
  135.                 if Update then begin
  136.                         SelectAll(false);
  137.                         DoOperation(EraseOp);
  138.                         UpdatePicWindow;
  139.                     end;
  140.                 UpdateTitleBar;
  141.                 if isRoi then
  142.                     RestoreRoi;
  143.                 WhatToUndo := NothingToUndo;
  144.                 AddSlice := true;
  145.                 changes := true;
  146.                 PictureType := NewPicture;
  147.                 UpdateWindowsMenuItem(PixMapSize * nSlices, title, PicNum);
  148.             end;
  149.     end;
  150.  
  151.  
  152.     procedure DeleteSlice;
  153.         var
  154.             SliceToDelete, NextSlice, i: integer;
  155.             isRoi: boolean;
  156.     begin
  157.         with info^, info^.StackInfo^ do begin
  158.                 if nSlices = 1 then begin
  159.                         WhatToUndo := NothingToUndo;
  160.                         exit(DeleteSlice);
  161.                     end;
  162.                 isRoi := RoiShowing;
  163.                 if isRoi then
  164.                     KillRoi;
  165.                 SetupUndo;
  166.                 WhatToUndo := UndoSliceDelete;
  167.                 SliceToDelete := CurrentSlice;
  168.                 if CurrentSlice = 1 then begin
  169.                         NextSlice := 2;
  170.                         WhatToUndo := UndoFirstSliceDelete;
  171.                     end
  172.                 else
  173.                     NextSlice := CurrentSlice - 1;
  174.                 SelectSlice(NextSlice);
  175.                 UpdatePicWindow;
  176.                 DisposHandle(PicBaseH[SliceToDelete]);
  177.                 for i := SliceToDelete to nSlices - 1 do
  178.                     PicBaseH[i] := PicBaseH[i + 1];
  179.                 nSlices := nSlices - 1;
  180.                 if CurrentSlice <> 1 then
  181.                     CurrentSlice := CurrentSlice - 1;
  182.                 UpdateTitleBar;
  183.                 if isRoi then
  184.                     RestoreRoi;
  185.                 changes := true;
  186.                 UpdateWindowsMenuItem(PixMapSize * nSlices, title, PicNum);
  187.             end;
  188.     end;
  189.  
  190.  
  191.     procedure MakeWindowsFromStack;
  192.         var
  193.             i, ignore, N: integer;
  194.             SaveInfo: InfoPtr;
  195.             tmp: longint;
  196.  
  197.         function MakeName (i: integer): str255;
  198.             var
  199.                 str: str255;
  200.         begin
  201.             RealToString(i, 3, 0, str);
  202.             if str[1] = ' ' then
  203.                 str[1] := '0';
  204.             if str[2] = ' ' then
  205.                 str[2] := '0';
  206.             MakeName := str;
  207.         end;
  208.  
  209.     begin
  210.         N := info^.StackInfo^.nSlices;
  211.         tmp := SizeOf(PicInfo);
  212.         if MaxBlock < (MinFree + info^.ImageSize + (SizeOf(PicInfo) + 2000) * LongInt(N)) then begin
  213.                 PutMessage('There is not enough memory available to convert this stack to windows.');
  214.                 exit(MakeWindowsFromStack);
  215.             end;
  216.         SaveInfo := Info;
  217.         KillRoi;
  218.         for i := 1 to N - 1 do begin
  219.                 SelectSlice(1);
  220.                 info^.StackInfo^.CurrentSlice := 1;
  221.                 if not Duplicate(MakeName(i), false) then
  222.                     exit(MakeWindowsFromStack);
  223.                 info := SaveInfo;
  224.                 DeleteSlice;
  225.             end;
  226.         if Duplicate(MakeName(N), false) then begin
  227.                 info := SaveInfo;
  228.                 info^.changes := false;
  229.                 ignore := CloseAWindow(info^.wptr);
  230.             end;
  231.     end;
  232.  
  233.  
  234.     procedure ShowNextSlice (item: integer);
  235.         var
  236.             isRoi: boolean;
  237.     begin
  238.         with info^, info^.StackInfo^ do begin
  239.                 if item = NextSliceItem then begin
  240.                         CurrentSlice := CurrentSlice + 1;
  241.                         if CurrentSlice > nSlices then
  242.                             CurrentSlice := nSlices;
  243.                     end
  244.                 else begin
  245.                         CurrentSlice := CurrentSlice - 1;
  246.                         if CurrentSlice < 1 then
  247.                             CurrentSlice := 1;
  248.                     end;
  249.                 isRoi := RoiShowing;
  250.                 if isRoi then
  251.                     KillRoi;
  252.                 SelectSlice(CurrentSlice);
  253.                 UpdatePicWindow;
  254.                 UpdateTitleBar;
  255.                 WhatToUndo := NothingToUndo;
  256.                 if isRoi then
  257.                     RestoreRoi;
  258.             end;
  259.     end;
  260.  
  261.  
  262.     procedure ShowFirstOrLastSlice (ich: integer);
  263.         var
  264.             isRoi: boolean;
  265.     begin
  266.         with info^, info^.StackInfo^ do begin
  267.                 if ich = EndKey then
  268.                     CurrentSlice := nSlices
  269.                 else
  270.                     CurrentSlice := 1;
  271.                 isRoi := RoiShowing;
  272.                 if isRoi then
  273.                     KillRoi;
  274.                 SelectSlice(CurrentSlice);
  275.                 UpdatePicWindow;
  276.                 UpdateTitleBar;
  277.                 WhatToUndo := NothingToUndo;
  278.                 if isRoi then
  279.                     RestoreRoi;
  280.             end;
  281.     end;
  282.  
  283.  
  284.     procedure DoResliceOptions;
  285.         var
  286.             default, tmp: extended;
  287.             Canceled: boolean;
  288.             prompt: str255;
  289.     begin
  290.         with info^.StackInfo^, info^ do begin
  291.                 if SliceSpacing = 0.0 then
  292.                     default := 1.0
  293.                 else begin
  294.                         if SpatiallyCalibrated then
  295.                             default := SliceSpacing / xSpatialScale
  296.                         else
  297.                             default := SliceSpacing;
  298.                     end;
  299.                 tmp := GetReal(concat('Slice Spacing(', xUnit, '):'), default, Canceled);
  300.                 if not Canceled and (tmp > 0.0) then begin
  301.                         if SpatiallyCalibrated then
  302.                             SliceSpacing := tmp * xSpatialScale
  303.                         else
  304.                             SliceSpacing := tmp;
  305.                     end;
  306.             end;
  307.     end;
  308.  
  309.  
  310.     procedure GetSlice (xstart, ystart, start: real; angle: extended; count: integer; var line: LineType);
  311.         var
  312.             i: integer;
  313.             x, y, xinc, yinc: extended;
  314.             IntegerStart: boolean;
  315.     begin
  316.         IntegerStart := (xstart = trunc(xstart)) and (ystart = trunc(ystart));
  317.         if IntegerStart and (angle = 0.0) then begin
  318.                 GetLine(trunc(xstart), trunc(ystart), count, line);
  319.                 exit(GetSlice);
  320.             end;
  321.         if IntegerStart and (angle = 270.0) then begin
  322.                 GetColumn(trunc(xstart), trunc(ystart), count, line);
  323.                 exit(GetSlice);
  324.             end;
  325.         angle := (angle / 180.0) * pi;
  326.         xinc := cos(angle);
  327.         yinc := -sin(angle);
  328.         x := xstart + start * xinc;
  329.         y := ystart + start * yinc;
  330.         for i := 0 to count - 1 do begin
  331.                 line[i] := round(GetInterpolatedPixel(x, y));
  332.                 x := x + xinc;
  333.                 y := y + yinc;
  334.             end;
  335.     end;
  336.  
  337.  
  338.     procedure Reslice;
  339.         var
  340.             DstWidth, DstHeight, nSlices: integer;
  341.             dstLeft, dstTop, y, i, j, LineLength: integer;
  342.             SaveWindowFlag, SaveMacro, HorizontalMode: boolean;
  343.             SaveHScale, SaveVScale, UncalibratedLineLength, CalibratedLineLength, angle: extended;
  344.             Stack, Reconstruction: InfoPtr;
  345.             aLine: LineType;
  346.             name, str1, str2: str255;
  347.             MaskRect: rect;
  348.             x1, y1, x2, y2, ulength, clength: real;
  349.  
  350.         procedure MakeRoi (Left, Top, Width, Height: integer);
  351.         begin
  352.             with info^ do begin
  353.                     RoiType := RectRoi;
  354.                     SetRect(RoiRect, left, top, left + width, top + height);
  355.                     MakeRegion;
  356.                     SetupUndo;
  357.                     RoiShowing := true;
  358.                 end;
  359.         end;
  360.  
  361.     begin
  362.         with info^, info^.StackInfo^ do begin
  363.                 if nSlices < 2 then begin
  364.                         PutMessage('Reslicing requires at least 2 slices.');
  365.                         macro := false;
  366.                         exit(Reslice);
  367.                     end;
  368.                 if not (RoiShowing and (RoiType = LineRoi)) then begin
  369.                         PutMessage('Please make a straight line selection first.');
  370.                         macro := false;
  371.                         exit(Reslice);
  372.                     end;
  373.                 Stack := info;
  374.                 GetLengthOrPerimeter(ulength, clength);
  375.                 LineLength := round(ulength);
  376.                 if LineLength = 0 then begin
  377.                         PutMessage('Line length cannot be zero.');
  378.                         macro := false;
  379.                         exit(Reslice);
  380.                     end;
  381.                 if SliceSpacing = 0.0 then
  382.                     DoResliceOptions;
  383.                 GetLoi(x1, y1, x2, y2);
  384.                 if (LAngle = 0.0) or (LAngle = 270.0) then
  385.                     if NotInBounds then
  386.                         exit(Reslice);
  387.                 HorizontalMode := not OptionKeyWasDown;
  388.                 if HorizontalMode then begin
  389.                         DstWidth := LineLength;
  390.                         DstHeight := round(nSlices * SliceSpacing);
  391.                         if DstHeight < nSlices then
  392.                             DstHeight := nSlices;
  393.                         dstLeft := 0;
  394.                         dstTop := round((dstHeight - nSlices) / 2);
  395.                     end
  396.                 else begin
  397.                         DstWidth := round(nSlices * SliceSpacing);
  398.                         if DstWidth < nSlices then
  399.                             DstWidth := nSlices;
  400.                         DstHeight := LineLength;
  401.                         dstLeft := round((dstWidth - nSlices) / 2);
  402.                         dstTop := 0;
  403.                     end;
  404.                 RealToString(y1, 3, 0, str1);
  405.                 RealToString(LAngle, 1, 2, str2);
  406.                 name := concat(str1, '-', str2);
  407.                 if not NewPicWindow(name, DstWidth, DstHeight) then
  408.                     exit(Reslice);
  409.                 Reconstruction := info;
  410.                 SaveWindowFlag := rsCreateNewWindow;
  411.                 SaveHScale := rsHScale;
  412.                 SaveVScale := rsVScale;
  413.                 rsCreateNewWindow := false;
  414.                 rsMethod := bilinear;
  415.                 for i := 1 to nSlices do begin
  416.                         Info := Stack;
  417.                         SelectSlice(i);
  418.                         GetSlice(x1, y1, 0.0, LAngle, LineLength, aLine);
  419.                         info := Reconstruction;
  420.                         if HorizontalMode then begin
  421.                                 PutLine(dstLeft, dstTop + nSlices - i, LineLength, aLine);
  422.                                 if i = 1 then {Draw extra line needed to get scaling to work right.}
  423.                                     PutLine(dstLeft, dstTop + nSlices, LineLength, aLine);
  424.                                 SetRect(MaskRect, dstLeft, dstTop + nSlices - i, dstLeft + LineLength, dstTop + nSlices - i + 1);
  425.                             end
  426.                         else begin
  427.                                 PutColumn(dstLeft + nSlices - i, dstTop, LineLength, aLine);
  428.                                 if i = 1 then {Draw extra line needed to get scaling to work right.}
  429.                                     PutLine(dstLeft + nSlices, dstTop, LineLength, aLine);
  430.                                 SetRect(MaskRect, dstLeft + nSlices - i, dstTop, dstLeft + nSlices - i + 1, dstTop + LineLength);
  431.                             end;
  432.                         UpdateScreen(MaskRect);
  433.                     end;
  434.                 if HorizontalMode then begin
  435.                         MakeRoi(dstLeft, dstTop, LineLength, nSlices);
  436.                         rsHScale := 1.0;
  437.                         rsVScale := SliceSpacing;
  438.                     end
  439.                 else begin
  440.                         MakeRoi(dstLeft, dstTop, nSlices, LineLength);
  441.                         rsHScale := SliceSpacing;
  442.                         rsVScale := 1.0;
  443.                     end;
  444.                 rsAngle := 0;
  445.                 SaveMacro := macro;
  446.                 macro := true;
  447.                 ScaleAndRotate;
  448.                 macro := SaveMacro;
  449.                 Info := Stack;
  450.                 SelectSlice(CurrentSlice);
  451.                 Info := Reconstruction;
  452.                 rsCreateNewWindow := SaveWindowFlag;
  453.                 rsHScale := SaveHScale;
  454.                 rsVScale := SaveVScale;
  455.                 KillRoi;
  456.             end;
  457.     end;
  458.  
  459.  
  460.     procedure Animate;
  461.         var
  462.             n, SaveN, fpsInterval, DelayCount: integer;
  463.             Event: EventRecord;
  464.             ch: char;
  465.             b: boolean;
  466.             SingleStep, GoForward, NewKeyDown, PhotoMode: boolean;
  467.             nFrames, StartTicks, NextTicks, SaveTicks, ticks: LongInt;
  468.             fps, seconds: extended;
  469.  
  470.         procedure ShowFPS (fps: extended);
  471.             var
  472.                 hstart, vstart, ivalue: integer;
  473.                 key: str255;
  474.         begin
  475.             if PhotoMode then
  476.                 exit(ShowFPS);
  477.             hstart := InfoHStart;
  478.             vstart := InfoVStart;
  479.             SetPort(InfoWindow);
  480.             MoveTo(xValueLoc, vstart);
  481.             case DelayTicks of
  482.                 0: 
  483.                     key := '9 ';
  484.                 2: 
  485.                     key := '8 ';
  486.                 3: 
  487.                     key := '7 ';
  488.                 4: 
  489.                     key := '6 ';
  490.                 6: 
  491.                     key := '5 ';
  492.                 8: 
  493.                     key := '4 ';
  494.                 12: 
  495.                     key := '3 ';
  496.                 30: 
  497.                     key := '2 ';
  498.                 60: 
  499.                     key := '1 ';
  500.             end;
  501.             if SingleStep then begin
  502.                     if GoForward then
  503.                         key := '->'
  504.                     else
  505.                         key := '<-';
  506.                 end;
  507.             DrawString(key);
  508.             MoveTo(yValueLoc, vstart + 10);
  509.             DrawReal(fps, 1, 2);
  510.             DrawChar(' ');
  511.         end;
  512.  
  513.     begin
  514.         if info^.StackInfo = nil then begin
  515.                 PutMessage('Animation requires a stack.');
  516.                 exit(Animate);
  517.             end;
  518.         with info^, info^.StackInfo^ do begin
  519.                 if nSlices < 2 then begin
  520.                         PutMessage('Animation requires at least two "slices".');
  521.                         exit(Animate);
  522.                     end;
  523.                 KillRoi;
  524.                 PhotoMode := OptionKeyDown or OptionKeyWasDown;
  525.                 if PhotoMode then
  526.                     EraseScreen
  527.                 else begin
  528.                         ShowWatch;
  529.                         ShowMessage(concat('Use 1...9 keys to control speed', cr, 'Use arrow keys to single step', cr, 'Press mouse button to stop'));
  530.                     end;
  531.                 FlushEvents(EveryEvent, 0);
  532.                 fpsInterval := 10;
  533.                 SaveN := -1;
  534.                 n := 1;
  535.                 GoForward := true;
  536.                 SingleStep := false;
  537.                 nFrames := 0;
  538.                 StartTicks := TickCount;
  539.                 NextTicks := StartTicks;
  540.                 SaveTicks := StartTicks;
  541.                 if not PhotoMode then begin
  542.                         DrawLabels('key:', 'fps:', '');
  543.                         SetPort(InfoWindow);
  544.                         TextSize(9);
  545.                         TextFont(Monaco);
  546.                         TextMode(SrcCopy);
  547.                     end;
  548.                 repeat
  549.                     b := WaitNextEvent(EveryEvent, Event, 0, nil);
  550.                     NewKeyDown := (event.what = KeyDown) or (event.what = AutoKey);
  551.                     if NewKeyDown then begin
  552.                             Ch := chr(BitAnd(Event.message, 127));
  553.                             SingleStep := false;
  554.                             case ord(ch) of
  555.                                 28, 44, 60, PageUp: {<-, <}
  556.                                     begin
  557.                                         SingleStep := true;
  558.                                         GoForward := false;
  559.                                         n := n - 1;
  560.                                         if n < 1 then
  561.                                             n := 1;
  562.                                         DelayTicks := 0
  563.                                     end; {left}
  564.                                 29, 46, 62, PageDown:  {->, >}
  565.                                     begin
  566.                                         SingleStep := true;
  567.                                         GoForward := true;
  568.                                         n := n + 1;
  569.                                         if n > nSlices then
  570.                                             n := nSlices;
  571.                                         DelayTicks := 0
  572.                                     end;  {right}
  573.                                 57: 
  574.                                     DelayTicks := 0;  {'9'-max speed}
  575.                                 56: 
  576.                                     DelayTicks := 2;  {'8'-30 fps}
  577.                                 55: 
  578.                                     DelayTicks := 3;  {'7'-20 fps}
  579.                                 54: 
  580.                                     DelayTicks := 4;  {'6'-15 fps}
  581.                                 53: 
  582.                                     DelayTicks := 6;  {'5'-10 fps}
  583.                                 52: 
  584.                                     DelayTicks := 8; {'4'-7.5 fps}
  585.                                 51: 
  586.                                     DelayTicks := 12; {'3'-5 fps}
  587.                                 50: 
  588.                                     DelayTicks := 30; {'2'-2 fps}
  589.                                 49: 
  590.                                     DelayTicks := 60; {'1'-1 fps}
  591.                                 otherwise
  592.                             end; {case}
  593.                             if DelayTicks > 12 then
  594.                                 fpsInterval := 2
  595.                             else if DelayTicks > 3 then
  596.                                 fpsInterval := 5
  597.                             else
  598.                                 fpsInterval := 10;
  599.                         end; {if NewKeyDown}
  600.                     if GoForward then begin
  601.                             if not SingleStep then
  602.                                 n := n + 1;
  603.                             if n > nSlices then begin
  604.                                     if OscillatingMovies then begin
  605.                                             n := nSlices - 1;
  606.                                             GoForward := false;
  607.                                         end
  608.                                     else
  609.                                         n := 1;
  610.                                 end;
  611.                         end
  612.                     else begin
  613.                             if not SingleStep then
  614.                                 n := n - 1;
  615.                             if n < 1 then begin
  616.                                     if OscillatingMovies then begin
  617.                                             n := 2;
  618.                                             Goforward := true;
  619.                                         end
  620.                                     else
  621.                                         n := nSlices;
  622.                                 end;
  623.                         end;
  624.                     CurrentSlice := n;
  625.                     SelectSlice(CurrentSlice);
  626.                     UpdatePicWindow;
  627.                     nFrames := nFrames + 1;
  628.                     if SingleStep then begin
  629.                             if (not OptionKeyWasDown) and (n <> SaveN) then begin
  630.                                     UpdateTitleBar;
  631.                                     SaveN := n;
  632.                                 end;
  633.                             ShowFPS(0.0);
  634.                         end
  635.                     else if (nFrames mod fpsInterval) = 0 then begin
  636.                             ticks := TickCount;
  637.                             seconds := (ticks - SaveTicks) / 60.0;
  638.                             if seconds <> 0.0 then
  639.                                 fps := fpsInterval / seconds
  640.                             else
  641.                                 fps := 0.0;
  642.                             ShowFPS(fps);
  643.                             SaveTicks := ticks;
  644.                         end;
  645.                     DelayCount := 0;
  646.                     if DelayTicks > 0 then begin
  647.                             repeat
  648.                                 ticks := TickCount;
  649.                             until ticks >= NextTicks;
  650.                             NextTicks := ticks + DelayTicks;
  651.                         end;
  652.                 until (event.what = MouseDown) or (event.what = osEvt);
  653.                 if PhotoMode then
  654.                     RestoreScreen;
  655.                 FlushEvents(EveryEvent, 0);
  656.             end; {with}
  657.     end;
  658.  
  659.  
  660.     procedure MakeMovie;
  661.         var
  662.             nFrames, wleft, wtop, width, height, frame, i: integer;
  663.             ignore, SaveFW: integer;
  664.             OutOfMemory: boolean;
  665.             DisplayPoint: point;
  666.             StartTicks, NextTicks, interval, ElapsedTime: LongInt;
  667.             SecondsBetweenFrames, seconds: extended;
  668.             frect: rect;
  669.             MainDevice: GDHandle;
  670.             SourcePixMap: PixMapHandle;
  671.             str1, str2, str3: str255;
  672.             Canceled: boolean;
  673.     begin
  674.         with info^ do begin
  675.                 if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin
  676.                         PutMessage('You must be capturing to make a movie.');
  677.                         exit(MakeMovie);
  678.                     end;
  679.                 StopDigitizing;
  680.                 if not (RoiShowing and (RoiType = RectRoi)) then begin
  681.                         PutMessage('Please make a rectangular selection first.');
  682.                         exit(MakeMovie);
  683.                     end;
  684.                 if NotInBounds then
  685.                     exit(MakeMovie);
  686.                 SaveFW := FramesWanted;
  687.                 FramesWanted := GetInt('Number of Frames:', FramesWanted, Canceled);
  688.                 if Canceled then begin
  689.                         FramesWanted := SaveFW;
  690.                         exit(MakeMovie);
  691.                     end;
  692.                 if FramesWanted < 1 then
  693.                     FramesWanted := 1;
  694.                 if FramesWanted > MaxSlices then
  695.                     FramesWanted := MaxSlices;
  696.                 with RoiRect do begin
  697.                         left := band(left + 1, $fffc);   {Word align}
  698.                         right := band(right + 2, $fffc);
  699.                         if right > PicRect.right then
  700.                             right := PicRect.right;
  701.                         MakeRegion;
  702.                         wleft := left;
  703.                         wtop := top;
  704.                         width := right - left;
  705.                         height := bottom - top;
  706.                     end;
  707.             end; {with info^}
  708.         if FrameGrabber = Scion then begin
  709.                 with DisplayPoint do begin
  710.                         h := PicLeftBase;
  711.                         v := PicTopBase;
  712.                     end;
  713.                 with frect do begin
  714.                         left := PicLeftBase + wleft;
  715.                         top := PicTopBase + wtop;
  716.                         right := left + width;
  717.                         bottom := top + height;
  718.                     end;
  719.             end
  720.         else
  721.             with frect do begin
  722.                     left := wleft;
  723.                     top := wtop;
  724.                     right := left + width;
  725.                     bottom := top + height;
  726.                 end;
  727.         if not NewPicWindow('Movie', width, height) then
  728.             exit(MakeMovie);
  729.         if not MakeStackFromWindow then
  730.             exit(MakeMovie);
  731.         nFrames := 1;
  732.         OutOfMemory := false;
  733.         while (nFrames < FramesWanted) and (not OutOfMemory) do begin
  734.                 OutOfMemory := not AddSlice(false);
  735.                 if not OutOfMemory then
  736.                     nFrames := nFrames + 1;
  737.             end;
  738.         if ExternalTrigger then
  739.             SecondsBetweenFrames := 0.0
  740.         else
  741.             SecondsBetweenFrames := GetReal('Delay Between Frames(seconds):', 0.0, Canceled);
  742.         if Canceled then
  743.             with info^ do begin
  744.                     changes := false;
  745.                     ignore := CloseAWindow(wptr);
  746.                     Exit(MakeMovie);
  747.                 end;
  748.         if SecondsBetweenFrames < 0.0 then
  749.             SecondsBetweenFrames := 0.0;
  750.         interval := round(60.0 * SecondsBetweenFrames);
  751.         if FrameGrabber = Scion then begin
  752.                 HideCursor;
  753.                 MainDevice := GetMainDevice;
  754.                 SourcePixMap := MainDevice^^.gdPMap;
  755.             end
  756.         else begin
  757.                 ShowWatch;
  758.                 SourcePixMap := fgPort^.portPixMap;
  759.                 ResetFrameGrabber;
  760.             end;
  761.         ShowTriggerMessage;
  762.         StartTicks := TickCount;
  763.         NextTicks := StartTicks;
  764.         with info^, info^.StackInfo^ do begin
  765.                 if Interval >= 30 then
  766.                     ShowMessage(CmdPeriodToStop)
  767.                 else
  768.                     DrawLabels('Frame:', 'Total:', '');
  769.                 for frame := 1 to nFrames do begin
  770.                         CurrentSlice := frame;
  771.                         SelectSlice(CurrentSlice);
  772.                         NextTicks := NextTicks + Interval;
  773.                         if FrameGrabber = Scion then begin
  774.                                 GetScionFrame(DisplayPoint);
  775.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  776.                             end
  777.                         else begin
  778.                                 if Interval >= 30 then
  779.                                     UpdateTitleBar
  780.                                 else
  781.                                     Show2Values(CurrentSlice, nSlices);
  782.                                 GetFrame;
  783.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  784.                                 if not BlindMovieCapture then
  785.                                     UpdatePicWindow;
  786.                             end;
  787.                         while TickCount < NextTicks do
  788.                             if CommandPeriod then begin
  789.                                     beep;
  790.                                     wait(60);
  791.                                     exit(MakeMovie);
  792.                                 end;
  793.                     end; {for}
  794.                 seconds := (TickCount - StartTicks) / 60.0;
  795.                 LoopTime := seconds;
  796.             end; {with}
  797.         RealToString(seconds, 1, 2, str1);
  798.         str1 := concat(long2str(nFrames), ' frames', cr, str1, ' seconds', cr);
  799.         RealToString(seconds / nFrames, 1, 3, str2);
  800.         str3 := concat(str1, str2, ' seconds/frame', cr);
  801.         if nFrames >= seconds then
  802.             ShowFrameRate(str3, StartTicks, nFrames)
  803.         else
  804.             ShowMessage(str3);
  805.         ShowFirstOrLastSlice(HomeKey);
  806.     end;
  807.  
  808.  
  809.     procedure CaptureFrames;
  810.         var
  811.             nFrames, wleft, wtop, width, height, i: integer;
  812.             ignore, SaveFW: integer;
  813.             OutOfMemory, AdvanceFrame, b: boolean;
  814.             DisplayPoint: point;
  815.             frect: rect;
  816.             MainDevice: GDHandle;
  817.             SourcePixMap: PixMapHandle;
  818.             Event: EventRecord;
  819.             ShutterSound: handle;
  820.             err: OSErr;
  821.  
  822.         procedure CheckButton;
  823.         begin
  824.             if Button and not AdvanceFrame then
  825.                 with Info^.StackInfo^ do begin
  826.                         AdvanceFrame := true;
  827.                         ShutterSound := GetResource('snd ', 100);
  828.                         if ShutterSound <> nil then
  829.                             err := SndPlay(nil, ShutterSound, false);
  830.                         if CurrentSlice < nSlices then begin
  831.                                 CurrentSlice := CurrentSlice + 1;
  832.                                 UpdateTitleBar;
  833.                                 CurrentSlice := CurrentSlice - 1;
  834.                             end;
  835.                     end;
  836.         end;
  837.  
  838.     begin
  839.         with info^ do begin
  840.                 if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin
  841.                         PutMessage('You must be capturing to capture frames.');
  842.                         exit(CaptureFrames);
  843.                     end;
  844.                 StopDigitizing;
  845.                 if not (RoiShowing and (RoiType = RectRoi)) then begin
  846.                         PutMessage('Please make a rectangular selection first.');
  847.                         exit(CaptureFrames);
  848.                     end;
  849.                 if NotInBounds then
  850.                     exit(CaptureFrames);
  851.                 SaveFW := FramesWanted;
  852.                 ShutterSound := nil;
  853.                 with RoiRect do begin
  854.                         left := band(left + 1, $fffc);   {Word align}
  855.                         right := band(right + 2, $fffc);
  856.                         if right > PicRect.right then
  857.                             right := PicRect.right;
  858.                         MakeRegion;
  859.                         wleft := left;
  860.                         wtop := top;
  861.                         width := right - left;
  862.                         height := bottom - top;
  863.                     end;
  864.             end; {with info^}
  865.         if FrameGrabber = Scion then begin
  866.                 with DisplayPoint do begin
  867.                         h := PicLeftBase;
  868.                         v := PicTopBase;
  869.                     end;
  870.                 with frect do begin
  871.                         left := PicLeftBase + wleft;
  872.                         top := PicTopBase + wtop;
  873.                         right := left + width;
  874.                         bottom := top + height;
  875.                     end;
  876.             end
  877.         else
  878.             with frect do begin
  879.                     left := wleft;
  880.                     top := wtop;
  881.                     right := left + width;
  882.                     bottom := top + height;
  883.                 end;
  884.         if not NewPicWindow('Frames', width, height) then
  885.             exit(CaptureFrames);
  886.         if not MakeStackFromWindow then
  887.             exit(CaptureFrames);
  888.         UpdateTitleBar;
  889.         if FrameGrabber = Scion then begin
  890.                 HideCursor;
  891.                 MainDevice := GetMainDevice;
  892.                 SourcePixMap := MainDevice^^.gdPMap;
  893.             end
  894.         else begin
  895.                 ShowWatch;
  896.                 SourcePixMap := fgPort^.portPixMap;
  897.                 ResetFrameGrabber;
  898.             end;
  899.         FlushEvents(EveryEvent, 0);
  900.         ExternalTrigger := false;
  901.         UpdateVideoControl;
  902.         with info^, info^.StackInfo^ do begin
  903.                 ShowMessage(CmdPeriodToStop);
  904.                 OutOfMemory := false;
  905.                 AdvanceFrame := false;
  906.                 while (not OutOfMemory) and (CurrentSlice <= MaxSlices) do begin
  907.                         if AdvanceFrame then begin
  908.                                 OutOfMemory := not AddSlice(false);
  909.                                 AdvanceFrame := false;
  910.                             end;
  911.                         if FrameGrabber = Scion then begin
  912.                                 GetScionFrame(DisplayPoint);
  913.                                 CheckButton;
  914.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  915.                                 CheckButton;
  916.                             end
  917.                         else begin
  918.                                 GetFrame;
  919.                                 CheckButton;
  920.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  921.                                 CheckButton;
  922.                                 UpdatePicWindow;
  923.                                 CheckButton;
  924.                             end;
  925.                         b := WaitNextEvent(EveryEvent, Event, 0, nil);
  926.                         if event.what = KeyDown then
  927.                             leave;
  928.                     end; {while}
  929.             end; {with}
  930.         if ShutterSound <> nil then
  931.             ReleaseResource(ShutterSound);
  932.     end;
  933.  
  934.  
  935.     procedure CopyPics (sPort, dPort: cGrafPtr; sRect, dRect: rect);
  936.     begin
  937.         pmForeColor(BlackIndex);
  938.         pmBackColor(WhiteIndex);
  939.         hlock(handle(sPort^.portPixMap));
  940.         hlock(handle(dPort^.portPixMap));
  941.         CopyBits(BitMapHandle(sPort^.portPixMap)^^, BitMapHandle(dPort^.PortPixMap)^^, sRect, dRect, SrcCopy, nil);
  942.         hunlock(handle(sPort^.portPixMap));
  943.         hunlock(handle(dPort^.PortPixMap));
  944.         pmForeColor(ForegroundIndex);
  945.         pmBackColor(BackgroundIndex);
  946.     end;
  947.  
  948.  
  949.     procedure MakeMontage;
  950.   {Opens a new window and creates a composite image}
  951.   {from the slices in the current stack.}
  952.         const
  953.             ColumnsID = 3;
  954.             RowsID = 4;
  955.             ScaleID = 5;
  956.             FirstID = 6;
  957.             LastID = 7;
  958.             IncrementID = 8;
  959.             NumberID = 9;
  960.         var
  961.             mylog: DialogPtr;
  962.             item, i, nRows, nColumns, Inc, slices: integer;
  963.             StackWidth, StackHeight, mWidth, mHeight, Background: integer;
  964.             dWidth, dHeight, dLeft, dTop, dRight, dBottom, MaxWidth, MaxHeight: integer;
  965.             FirstSlice, LastSlice, TotalSlices: integer;
  966.             scale, SaveScale: extended;
  967.             sPort, dPort: cGrafPtr;
  968.             StackInfo, MontageInfo: InfoPtr;
  969.             sRect, dRect: rect;
  970.             NumberSlices, IncrementSet: boolean;
  971.             str: str255;
  972.             loc: point;
  973.             SaveGDevice: GDHandle;
  974.  
  975.         procedure Estimate (adjustinc: boolean);
  976.             var
  977.                 tmp, xScale, yScale: extended;
  978.                 n: integer;
  979.         begin
  980.             slices := LastSlice - FirstSlice + 1;
  981.             if adjustinc then
  982.                 inc := 0;
  983.             repeat
  984.                 if adjustinc then
  985.                     inc := inc + 1;
  986.                 n := trunc(slices / inc);
  987.                 tmp := sqrt(n);
  988.                 if trunc(tmp) <> tmp then
  989.                     tmp := trunc(tmp) + 1.0;
  990.                 nColumns := trunc(tmp);
  991.                 nRows := nColumns;
  992.                 if (nColumns * (nRows - 1)) >= n then
  993.                     nRows := nRows - 1;
  994.                 xScale := (MaxWidth / nColumns) / StackWidth;
  995.                 yScale := (MaxHeight / nRows) / StackHeight;
  996.                 if xScale < yScale then
  997.                     scale := xScale
  998.                 else
  999.                     scale := yScale;
  1000.                 if scale > 1.0 then
  1001.                     scale := 1.0;
  1002.                 SaveScale := scale;
  1003.             until (scale >= 0.5) or (inc >= 3) or not adjustinc;
  1004.         end;
  1005.  
  1006.     begin
  1007.         InitCursor;
  1008.         with info^ do begin
  1009.                 StackWidth := PixelsPerLine;
  1010.                 StackHeight := nLines;
  1011.                 FirstSlice := 1;
  1012.                 TotalSlices := StackInfo^.nSlices;
  1013.                 LastSlice := TotalSlices;
  1014.             end;
  1015.         MaxWidth := ScreenWidth - 85;
  1016.         MaxHeight := ScreenHeight - 45;
  1017.         Estimate(true);
  1018.         NumberSlices := true;
  1019.         IncrementSet := false;
  1020.         mylog := GetNewDialog(150, nil, pointer(-1));
  1021.         SetDNum(MyLog, RowsID, nRows);
  1022.         SetDNum(MyLog, ColumnsID, nColumns);
  1023.         SetDReal(MyLog, ScaleID, scale, 2);
  1024.         SetDNum(MyLog, FirstID, FirstSlice);
  1025.         SetDNum(MyLog, LastID, LastSlice);
  1026.         SetDNum(MyLog, IncrementID, inc);
  1027.         SetDialogItem(MyLog, NumberID, ord(NumberSlices));
  1028.         OutlineButton(MyLog, ok, 16);
  1029.         repeat
  1030.             ModalDialog(nil, item);
  1031.             if item = ColumnsID then begin
  1032.                     nColumns := GetDNum(MyLog, ColumnsID);
  1033.                     if nColumns < 0 then begin
  1034.                             nColumns := 0;
  1035.                             SetDNum(MyLog, ColumnsID, nRows);
  1036.                         end;
  1037.                 end;
  1038.             if item = RowsID then begin
  1039.                     nRows := GetDNum(MyLog, RowsID);
  1040.                     if nRows < 0 then begin
  1041.                             nRows := 0;
  1042.                             SetDNum(MyLog, RowsID, nRows);
  1043.                         end;
  1044.                 end;
  1045.             if item = ScaleID then
  1046.                 scale := GetDReal(MyLog, ScaleID);
  1047.             if item = FirstID then begin
  1048.                     FirstSlice := GetDNum(MyLog, FirstID);
  1049.                     if (FirstSlice < 1) or (FirstSlice > LastSlice) then
  1050.                         FirstSlice := 1;
  1051.                     if IncrementSet then
  1052.                         Estimate(false)
  1053.                     else
  1054.                         Estimate(true);
  1055.                     SetDNum(MyLog, RowsID, nRows);
  1056.                     SetDNum(MyLog, ColumnsID, nColumns);
  1057.                     SetDReal(MyLog, ScaleID, scale, 2);
  1058.                 end;
  1059.             if item = LastID then begin
  1060.                     LastSlice := GetDNum(MyLog, LastID);
  1061.                     if (LastSlice < FirstSlice) or (LastSlice > TotalSlices) then
  1062.                         LastSlice := TotalSlices;
  1063.                     if IncrementSet then
  1064.                         Estimate(false)
  1065.                     else
  1066.                         Estimate(true);
  1067.                     SetDNum(MyLog, RowsID, nRows);
  1068.                     SetDNum(MyLog, ColumnsID, nColumns);
  1069.                     SetDReal(MyLog, ScaleID, scale, 2);
  1070.                 end;
  1071.             if item = IncrementID then begin
  1072.                     inc := GetDNum(MyLog, IncrementID);
  1073.                     IncrementSet := true;
  1074.                     if (inc < 1) or (inc > (slices div 2)) then begin
  1075.                             inc := 1;
  1076.                             SetDNum(MyLog, IncrementID, inc);
  1077.                         end;
  1078.                     Estimate(false);
  1079.                     SetDNum(MyLog, RowsID, nRows);
  1080.                     SetDNum(MyLog, ColumnsID, nColumns);
  1081.                     SetDReal(MyLog, ScaleID, scale, 2);
  1082.                 end;
  1083.             if item = NumberID then begin
  1084.                     NumberSlices := not NumberSlices;
  1085.                     SetDialogItem(MyLog, NumberID, ord(NumberSlices));
  1086.                 end;
  1087.         until (item = ok) or (item = cancel);
  1088.         DisposDialog(mylog);
  1089.         if item = cancel then
  1090.             exit(MakeMontage);
  1091.         if (scale <= 0.05) or (scale > 5) then
  1092.             scale := SaveScale;
  1093.         dWidth := round(StackWidth * scale);
  1094.         dHeight := round(StackHeight * scale);
  1095.         mWidth := nColumns * dWidth;
  1096.         mHeight := nRows * dHeight;
  1097.         StackInfo := info;
  1098.         Background := MyGetPixel(0, 0);
  1099.         SetBackgroundColor(Background);
  1100.         if Background = WhiteIndex then
  1101.             SetForegroundColor(BlackIndex)
  1102.         else
  1103.             SetForegroundColor(WhiteIndex);
  1104.         if not NewPicWindow('Montage', mWidth, mHeight) then
  1105.             exit(MakeMontage);
  1106.         MontageInfo := info;
  1107.         SaveGDevice := GetGDevice;
  1108.         SetGDevice(osGDevice);
  1109.         if NumberSlices then begin
  1110.                 SetPort(GrafPtr(info^.osPort));
  1111.                 pmForeColor(ForegroundIndex);
  1112.                 TextFont(ApplFont);
  1113.                 TextSize(9);
  1114.             end;
  1115.         dPort := info^.osPort;
  1116.         dLeft := 0;
  1117.         dTop := 0;
  1118.         sPort := StackInfo^.osPort;
  1119.         sRect := StackInfo^.PicRect;
  1120.         i := FirstSlice;
  1121.         while i <= LastSlice do begin
  1122.                 Info := StackInfo;
  1123.                 SelectSlice(i);
  1124.                 SetRect(dRect, dLeft, dTop, dLeft + dWidth, dTop + DHeight);
  1125.                 CopyPics(sPort, dPort, sRect, dRect);
  1126.                 info := MontageInfo;
  1127.                 if NumberSlices then begin
  1128.                         MoveTo(dLeft + (dWidth div 2) - 3, dTop + dHeight - 9);
  1129.                         NumToString(i, str);
  1130.                         loc.h := dLeft + (dWidth div 2) - 3;
  1131.                         loc.v := dTop + dHeight - 5;
  1132.                         DrawTextString(str, loc, TeJustCenter);
  1133.                     end;
  1134.                 UpdateScreen(dRect);
  1135.                 dLeft := dLeft + dWidth;
  1136.                 if (dLeft + dWidth) > mWidth then begin
  1137.                         dLeft := 0;
  1138.                         dTop := dTop + dHeight;
  1139.                     end;
  1140.                 i := i + inc;
  1141.             end;
  1142.         SetGDevice(SaveGDevice);
  1143.         info := StackInfo;
  1144.         SelectSlice(info^.StackInfo^.CurrentSlice);
  1145.         if MontageInfo^.PixMapSize > UndoBufSize then
  1146.             PutWarning;
  1147.     end;
  1148.  
  1149.  
  1150.     procedure CopyRGBToPixMap (pmap: PixMapHandle);
  1151.         type
  1152.             LongPtr = ^LongInt;
  1153.         var
  1154.             row, i, width, WatchRate: integer;
  1155.             RedLine, GreenLine, BlueLine: LineType;
  1156.             Pixel, RowOffset: LongInt;
  1157.             pmapPtr: ptr;
  1158.             LPtr, RowStart: LongPtr;
  1159.     begin
  1160.         with info^ do begin
  1161.                 pmapPtr := GetPixBaseAddr(pmap);
  1162.                 if pmapPtr = nil then
  1163.                     exit(CopyRGBToPixMap);
  1164.                 LPtr := LongPtr(pmapPtr);
  1165.                 RowStart := LPtr;
  1166.                 RowOffset := band(pmap^^.RowBytes, $3FFF);
  1167.                 width := PicRect.right;
  1168.                 WatchRate := 20000 div PixelsPerLine;
  1169.                 for row := 0 to nLines - 1 do begin
  1170.                         if (row mod WatchRate) = 0 then
  1171.                             ShowAnimatedWatch;
  1172.                         SelectSlice(1);
  1173.                         GetLine(0, row, width, RedLine);
  1174.                         SelectSlice(2);
  1175.                         GetLine(0, row, width, GreenLine);
  1176.                         SelectSlice(3);
  1177.                         GetLine(0, row, width, BlueLine);
  1178.                         LPtr := RowStart;
  1179.                         for i := 0 to PixelsPerLine - 1 do begin
  1180.                                 pixel := -1;
  1181.                                 pixel := RedLine[i];
  1182.                                 pixel := bor(bsl(pixel, 8), GreenLine[i]);
  1183.                                 pixel := bor(bsl(pixel, 8), blueLine[i]);
  1184.                                 LPtr^ := BitNot(pixel);
  1185.                                 LPtr := LongPtr(ord4(LPtr) + 4);
  1186.                             end;
  1187.                         RowStart := LongPtr(ord4(RowStart) + RowOffset);
  1188.                     end;
  1189.                 SelectSlice(StackInfo^.CurrentSlice);
  1190.             end; {with}
  1191.     end;
  1192.  
  1193.  
  1194.     function DoColorOptions: boolean;
  1195.         const
  1196.             ExistingID = 4;
  1197.             SystemID = 5;
  1198.             CustomID = 6;
  1199.             DitherID = 7;
  1200.         var
  1201.             mylog: DialogPtr;
  1202.             item: integer;
  1203.  
  1204.         procedure UpdateButtons;
  1205.         begin
  1206.             SetDialogItem(mylog, ExistingID, ord(RGBLut = ExistingLUT));
  1207.             SetDialogItem(mylog, SystemID, ord(RGBLut = SystemLUT));
  1208.             SetDialogItem(mylog, CustomID, ord(RGBLut = CustomLUT));
  1209.         end;
  1210.  
  1211.     begin
  1212.         InitCursor;
  1213.         mylog := GetNewDialog(160, nil, pointer(-1));
  1214.         SetDialogItem(mylog, DitherID, ord(DitherColor));
  1215.         UpdateButtons;
  1216.         OutlineButton(MyLog, ok, 16);
  1217.         repeat
  1218.             ModalDialog(nil, item);
  1219.             if item = DitherID then begin
  1220.                     DitherColor := not DitherColor;
  1221.                     SetDialogItem(mylog, DitherID, ord(DitherColor));
  1222.                 end;
  1223.             if item = ExistingID then begin
  1224.                     RGBLut := ExistingLUT;
  1225.                     UpdateButtons
  1226.                 end;
  1227.             if item = SystemID then begin
  1228.                     RGBLut := SystemLUT;
  1229.                     UpdateButtons;
  1230.                     DitherColor := true;
  1231.                     SetDialogItem(mylog, DitherID, ord(DitherColor));
  1232.                 end;
  1233.             if item = CustomID then begin
  1234.                     RGBLut := CustomLUT;
  1235.                     UpdateButtons
  1236.                 end;
  1237.         until (item = ok) or (item = cancel);
  1238.         DisposDialog(mylog);
  1239.         DoColorOptions := item <> cancel;
  1240.     end;
  1241.  
  1242.  
  1243.  
  1244.     function Activate (name: str255): boolean;
  1245.   {Activates the window with the specified name.}
  1246.         var
  1247.             i: integer;
  1248.             TempInfo: InfoPtr;
  1249.     begin
  1250.         Activate := false;
  1251.         for i := 1 to nPics do begin
  1252.                 TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
  1253.                 if TempInfo^.title = name then begin
  1254.                         if PicWindow[i] <> nil then begin
  1255.                                 SelectWindow(PicWindow[i]);
  1256.                                 Info := TempInfo;
  1257.                                 ActivateWindow;
  1258.                                 Activate := true;
  1259.                             end; {if}
  1260.                         leave;
  1261.                     end; {if}
  1262.             end; {for}
  1263.     end;
  1264.  
  1265.  
  1266.     procedure ConvertRGBToEightBitColor (Capturing: boolean);
  1267.         var
  1268.             err: QDErr;
  1269.             err2: OSErr;
  1270.             osGWorld: GWorldPtr;
  1271.             flags: GWorldFlags;
  1272.             pmap: PixMapHandle;
  1273.             pRect: rect;
  1274.             thePictInfo: PictInfo;
  1275.             CopyMode, SamplingMethod: integer;
  1276.             UpdateNeeded: boolean;
  1277.             SaveGDevice: GDHandle;
  1278.  
  1279.         procedure abort;
  1280.         begin
  1281.             DisposeGWorld(osGWorld);
  1282.             exit(ConvertRGBToEightBitColor);
  1283.         end;
  1284.  
  1285.     begin
  1286.         if not System7 then begin
  1287.                 PutMessage('You must be running System 7 to do 24 to 8-bit color conversions.');
  1288.                 exit(ConvertRGBToEightBitColor);
  1289.             end;
  1290.         with info^ do begin
  1291.                 if StackInfo^.nSlices <> 3 then begin
  1292.                         PutMessage('24 to 8-bit color conversion requires a three slice(red, green and blue) stack as input.');
  1293.                         exit(ConvertRGBToEightBitColor);
  1294.                     end;
  1295.                 if Capturing then begin
  1296.                         DitherColor := true;
  1297.                         RGBLut := CustomLUT;
  1298.                     end
  1299.                 else if not macro then begin
  1300.                         if not DoColorOptions then
  1301.                             exit(ConvertRGBToEightBitColor);
  1302.                     end;
  1303.                 flags := [];
  1304.                 err := NewGWorld(osGWorld, 32, PicRect, nil, nil, flags);
  1305.                 if err <> NoErr then begin
  1306.                         PutMemoryAlert;
  1307.                         exit(ConvertRGBToEightBitColor);
  1308.                     end;
  1309.                 pmap := GetGWorldPixMap(osGWorld);
  1310.                 if not LockPixels(pmap) then
  1311.                     abort;
  1312.                 CopyRGBToPixMap(pmap);
  1313.                 pRect := PicRect;
  1314.             end; {with}
  1315.         UpdateNeeded := true;
  1316.         if Activate('Indexed Color') then begin
  1317.                 if (info^.PixelsPerLine <> pRect.right) or (info^.nLines <> pRect.bottom) then begin
  1318.                         if not NewPicWindow('Indexed Color', pRect.right, pRect.bottom) then
  1319.                             abort;
  1320.                         UpdateNeeded := false;
  1321.                     end
  1322.             end
  1323.         else begin
  1324.                 if not NewPicWindow('Indexed Color', pRect.right, pRect.bottom) then
  1325.                     abort;
  1326.                 UpdateNeeded := false;
  1327.             end;
  1328.         if RGBLut = SystemLUT then
  1329.             SwitchColorTables(SystemPaletteItem, false)
  1330.         else if RGBLut = CustomLut then begin
  1331.                 if OptionKeyWasDown then
  1332.                     SamplingMethod := PopularMethod
  1333.                 else
  1334.                     SamplingMethod := SystemMethod;
  1335.                 err2 := GetPixMapInfo(pmap, thePictInfo, ReturnColorTable, 256, SamplingMethod, 0);
  1336.                 LoadColorTable(thePictInfo.theColorTable);
  1337.             end;
  1338.         SetForegroundColor(BlackIndex);
  1339.         SetBackgroundColor(WhiteIndex);
  1340.         if DitherColor then
  1341.             CopyMode := DitherCopy
  1342.         else
  1343.             CopyMode := SrcCopy;
  1344.         SaveGDevice := GetGDevice;
  1345.         SetGDevice(osGDevice);
  1346.         SetPort(GrafPtr(Info^.osPort));
  1347.         CopyBits(BitMapHandle(pmap)^^, BitMapHandle(info^.osPort^.PortPixMap)^^, pRect, pRect, CopyMode, nil);
  1348.         SetGDevice(SaveGDevice);
  1349.         DisposeGWorld(osGWorld);
  1350.         if UpdateNeeded then
  1351.             UpdatePicWindow;
  1352.     end;
  1353.  
  1354.  
  1355.     function MakeRGBStack (name: str255): boolean;
  1356.         var
  1357.             ignore: integer;
  1358.     begin
  1359.         MakeRGBStack := false;
  1360.         if not Duplicate(name, false) then
  1361.             exit(MakeRGBStack);
  1362.         if not MakeStackFromWindow then
  1363.             exit(MakeRGBStack);
  1364.         if not AddSlice(false) then begin
  1365.                 info^.changes := false;
  1366.                 ignore := CloseAWindow(info^.wptr);
  1367.                 exit(MakeRGBStack);
  1368.             end;
  1369.         if not AddSlice(false) then begin
  1370.                 info^.changes := false;
  1371.                 ignore := CloseAWindow(info^.wptr);
  1372.                 exit(MakeRGBStack);
  1373.             end;
  1374.         MakeRGBStack := true;
  1375.     end;
  1376.  
  1377.  
  1378.     procedure ConvertEightBitColorToRGB;
  1379.         var
  1380.             width, height, i, row: integer;
  1381.             srcLine, rLine, gLine, bLine: LineType;
  1382.             rLut, gLUT, bLUT: packed array[0..255] of byte;
  1383.             value: byte;
  1384.     begin
  1385.         if isGrayscaleLUT then begin
  1386.                 PutMessage('8-bit color to RGB conversion requires a color image.');
  1387.                 exit(ConvertEightBitColorToRGB);
  1388.             end;
  1389.         KillRoi;
  1390.         if not MakeRGBStack(concat(info^.title, '(RGB)')) then
  1391.             exit(ConvertEightBitColorToRGB);
  1392.         LoadLUT(Info^.cTable);
  1393.         for i := 0 to 255 do
  1394.             with cScreenPort^.portPixMap^^.pmTable^^.ctTable[i].rgb do begin
  1395.                     rLUT[i] := BitNot(band(bsr(red, 8), 255));
  1396.                     gLUT[i] := BitNot(band(bsr(green, 8), 255));
  1397.                     bLUT[i] := BitNot(band(bsr(blue, 8), 255));
  1398.                 end;
  1399.         width := info^.PixelsPerLine;
  1400.         height := info^.nLines;
  1401.         for row := 0 to height - 1 do begin
  1402.                 SelectSlice(1);
  1403.                 GetLine(0, row, width, srcLine);
  1404.                 for i := 0 to width - 1 do begin
  1405.                         value := srcLine[i];
  1406.                         rLine[i] := rLUT[value];
  1407.                         gLine[i] := gLUT[value];
  1408.                         bLine[i] := bLUT[value];
  1409.                     end;
  1410.                 PutLine(0, row, width, rLine);
  1411.                 SelectSlice(2);
  1412.                 PutLine(0, row, width, gLine);
  1413.                 SelectSlice(3);
  1414.                 PutLine(0, row, width, bLine);
  1415.             end;
  1416.         with Info^.StackInfo^ do begin
  1417.                 CurrentSlice := 1;
  1418.                 SelectSlice(CurrentSlice);
  1419.                 UpdateTitleBar;
  1420.             end;
  1421.     end;
  1422.  
  1423.  
  1424.     procedure CaptureColor;
  1425.         var
  1426.             MainDevice: GDHandle;
  1427.             SourcePixMap: PixMapHandle;
  1428.             frame, width, height, SaveChannel: integer;
  1429.             frect: rect;
  1430.             DisplayPoint: point;
  1431.     begin
  1432.         with info^ do
  1433.             if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin
  1434.                     PutMessage('You must be capturing to capture color.');
  1435.                     macro := false;
  1436.                     exit(CaptureColor);
  1437.                 end;
  1438.         StopDigitizing;
  1439.         with info^.PicRect do begin
  1440.                 width := right - left;
  1441.                 height := bottom - top;
  1442.             end;
  1443.         if Activate('RGB') then
  1444.             with info^.PicRect do begin
  1445.                     if ((right - left) <> width) or ((bottom - top) <> height) then
  1446.                         if not MakeRGBStack('RGB') then
  1447.                             exit(CaptureColor);
  1448.                 end
  1449.         else if not MakeRGBStack('RGB') then
  1450.             exit(CaptureColor);
  1451.         if FrameGrabber = Scion then begin
  1452.                 HideCursor;
  1453.                 MainDevice := GetMainDevice;
  1454.                 SourcePixMap := MainDevice^^.gdPMap;
  1455.             end
  1456.         else begin
  1457.                 ShowWatch;
  1458.                 SourcePixMap := fgPort^.portPixMap;
  1459.                 ResetFrameGrabber;
  1460.             end;
  1461.         if FrameGrabber = Scion then begin
  1462.                 with DisplayPoint do begin
  1463.                         h := PicLeftBase;
  1464.                         v := PicTopBase;
  1465.                     end;
  1466.                 with frect do begin
  1467.                         left := PicLeftBase;
  1468.                         top := PicTopBase;
  1469.                         right := left + width;
  1470.                         bottom := top + height;
  1471.                     end;
  1472.             end
  1473.         else
  1474.             with frect do begin
  1475.                     left := 0;
  1476.                     top := 0;
  1477.                     right := left + width;
  1478.                     bottom := top + height;
  1479.                 end;
  1480.         ShowTriggerMessage;
  1481.         SaveChannel := VideoChannel;
  1482.         with info^, info^.StackInfo^ do begin
  1483.                 for frame := 1 to 3 do begin
  1484.                         if FrameGrabber = QuickCapture then begin
  1485.                                 case frame of
  1486.                                     1: 
  1487.                                         VideoChannel := 1; {Green}
  1488.                                     2: 
  1489.                                         VideoChannel := 0;  {Red}
  1490.                                     3: 
  1491.                                         VideoChannel := 2;  {Blue}
  1492.                                 end;
  1493.                                 ResetFrameGrabber;
  1494.                                 repeat
  1495.                                 until band(ControlReg^, $8) = 0; {mux channel not busy}
  1496.                             end
  1497.                         else begin
  1498.                                 VideoChannel := frame - 1;
  1499.                                 ResetFrameGrabber;
  1500.                             end;
  1501.                         if VideoControl <> nil then
  1502.                             ShowChannel;
  1503.                         CurrentSlice := frame;
  1504.                         SelectSlice(CurrentSlice);
  1505.                         if FrameGrabber = Scion then begin
  1506.                                 GetScionFrame(DisplayPoint);
  1507.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  1508.                             end
  1509.                         else begin
  1510.                                 GetFrame;
  1511.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  1512.                             end;
  1513.                     end; {for}
  1514.                 CurrentSlice := 1;
  1515.                 SelectSlice(CurrentSlice);
  1516.                 UpdateTitleBar;
  1517.             end; {with}
  1518.         VideoChannel := SaveChannel;
  1519.         if VideoControl <> nil then
  1520.             ShowChannel;
  1521.         ConvertRGBToEightBitColor(true);
  1522.     end;
  1523.  
  1524.  
  1525.     procedure AverageSlices;
  1526.         const
  1527.             MaxWidth = 2048;
  1528.         var
  1529.             slices, sRow, aRow, slice, i, SaveSlice: integer;
  1530.             width, height, hstart, vStart: integer;
  1531.             OldInfo, NewInfo: InfoPtr;
  1532.             aLine: LineType;
  1533.             mask: rect;
  1534.             sum: array[0..MaxWidth] of LongInt;
  1535.             AutoSelectAll: boolean;
  1536.     begin
  1537.         OldInfo := Info;
  1538.         with info^ do begin
  1539.                 if StackInfo = nil then begin
  1540.                         PutMessage('Average Slices requires a stack.');
  1541.                         macro := false;
  1542.                         exit(AverageSlices);
  1543.                     end;
  1544.                 AutoSelectAll := not Info^.RoiShowing;
  1545.                 if AutoSelectAll then
  1546.                     SelectAll(true);
  1547.                 with RoiRect do begin
  1548.                         hStart := left;
  1549.                         vStart := top;
  1550.                         width := right - left;
  1551.                         height := bottom - top;
  1552.                     end;
  1553.                 if width > MaxWidth then begin
  1554.                         PutMessage(concat('NIH Image can''t average selections wider than ', Long2str(MaxWidth), ' pixels.'));
  1555.                         macro := false;
  1556.                         exit(AverageSlices);
  1557.                     end;
  1558.                 with StackInfo^ do begin
  1559.                         slices := StackInfo^.nSlices;
  1560.                         SaveSlice := CurrentSlice;
  1561.                     end;
  1562.                 if not NewPicWindow('Average', width, height) then begin
  1563.                         macro := false;
  1564.                         exit(AverageSlices);
  1565.                     end;
  1566.             end;
  1567.         info^.changes := true;
  1568.         NewInfo := Info;
  1569.         aRow := 0;
  1570.         for sRow := vStart to vStart + height - 1 do begin
  1571.                 info := OldInfo;
  1572.                 for i := 0 to width - 1 do
  1573.                     sum[i] := 0;
  1574.                 for slice := 1 to slices do begin
  1575.                         SelectSlice(slice);
  1576.                         GetLine(hStart, sRow, width, aLine);
  1577.                         for i := 0 to width - 1 do
  1578.                             sum[i] := sum[i] + aLine[i];
  1579.                     end;
  1580.                 for i := 0 to width - 1 do
  1581.                     aLine[i] := sum[i] div slices;
  1582.                 info := NewInfo;
  1583.                 PutLine(0, aRow, width, aLine);
  1584.                 SetRect(mask, 0, aRow, width, aRow + 1);
  1585.                 aRow := aRow + 1;
  1586.                 UpdateScreen(mask);
  1587.                 if CommandPeriod then
  1588.                     leave;
  1589.             end;
  1590.         info := OldInfo;
  1591.         SelectSlice(SaveSlice);
  1592.         if AutoSelectAll then
  1593.             KillRoi;
  1594.     end;
  1595.  
  1596.  
  1597.     procedure ConvertRGBToHSV;
  1598.         const
  1599.             MaxSaturation = 255;
  1600.             MaxValue = 255;
  1601.         var
  1602.             width, height, i, row, mark: integer;
  1603.             rLine, gLine, bLine, hLine, sLine, vLine: LineType;
  1604.             delta, min, max, R, G, B, H, S, V: integer;
  1605.             tmp: longint;
  1606.             UpdateR: rect;
  1607.  
  1608.         function Max3 (a, b, c: integer): integer;
  1609.             var
  1610.                 TempMax: integer;
  1611.         begin
  1612.             if (a > b) then
  1613.                 TempMax := a
  1614.             else
  1615.                 TempMax := b;
  1616.             if (TempMax > c) then
  1617.                 Max3 := TempMax
  1618.             else
  1619.                 Max3 := c;
  1620.         end;
  1621.  
  1622.         function Min3 (a, b, c: integer): integer;
  1623.             var
  1624.                 TempMin: integer;
  1625.         begin
  1626.             if (a < b) then
  1627.                 TempMin := a
  1628.             else
  1629.                 TempMin := b;
  1630.             if (TempMin < c) then
  1631.                 Min3 := TempMin
  1632.             else
  1633.                 Min3 := c;
  1634.         end;
  1635.  
  1636.     begin
  1637.         with info^ do begin
  1638.                 if StackInfo^.nSlices <> 3 then begin
  1639.                         PutMessage('RGB to HSV color conversion requires a three slice(red, green and blue) stack as input.');
  1640.                         exit(ConvertRGBToHSV);
  1641.                     end;
  1642.                 if Changes then begin
  1643.                         if PutMessageWithCancel('RGB to HSV color conversion is undoable.') = cancel then
  1644.                             exit(ConvertRGBToHSV);
  1645.                     end;
  1646.                 KillRoi;
  1647.                 with StackInfo^ do begin
  1648.                         CurrentSlice := 1;
  1649.                         SelectSlice(CurrentSlice);
  1650.                         UpdatePicWindow;
  1651.                     end;
  1652.                 SwitchColorTables(SpectrumItem, true);
  1653.                 title := 'HSV';
  1654.                 UpdateTitleBar;
  1655.                 width := PixelsPerLine;
  1656.                 height := nLines;
  1657.                 mark := 0;
  1658.                 ShowWatch;
  1659.                 for row := 0 to height - 1 do begin
  1660.                         SelectSlice(1);
  1661.                         GetLine(0, row, width, rLine);
  1662.                         SelectSlice(2);
  1663.                         GetLine(0, row, width, gLine);
  1664.                         SelectSlice(3);
  1665.                         GetLine(0, row, width, bLine);
  1666.                         for i := 0 to width - 1 do begin
  1667.                                 R := 255 - rLine[i];
  1668.                                 G := 255 - gLine[i];
  1669.                                 B := 255 - bLine[i];
  1670.                                 max := Max3(R, G, B);
  1671.                                 min := Min3(R, G, B);
  1672.                                 V := max;
  1673.                                 if max <> 0 then begin
  1674.                                         tmp := 255 * (max - min);
  1675.                                         S := (tmp + (tmp mod max)) div max;  {adding '(tmp mod max)' simulate rounding}
  1676.                                     end
  1677.                                 else
  1678.                                     S := 0;
  1679.                                 if S = 0 then
  1680.                                     H := 0  {undefined but, but select red }
  1681.                                 else begin
  1682.                                         delta := max - min;
  1683.                                         if R = max then begin
  1684.                                                 tmp := 85 * (G - B);
  1685.                                                 H := tmp div delta;
  1686.                                             end
  1687.                                         else if G = max then begin
  1688.                                                 tmp := 85 * (B - R);
  1689.                                                 H := 170 + tmp div delta;
  1690.                                             end
  1691.                                         else if B = max then begin
  1692.                                                 tmp := 85 * (R - G);
  1693.                                                 H := 340 + tmp div delta;
  1694.                                             end;
  1695.                                         H := H div 2;
  1696.                                         if H < 0 then
  1697.                                             H := H + 255
  1698.                                     end;
  1699.                                 if H = 0 then
  1700.                                     hLine[i] := 1
  1701.                                 else
  1702.                                     hLine[i] := H;
  1703.                                 sLine[i] := S;
  1704.                                 vLine[i] := 255 - V;
  1705.                             end;
  1706.                         SelectSlice(1);
  1707.                         PutLine(0, row, width, hLine);
  1708.                         if (row mod 10) = 0 then begin
  1709.                                 setrect(UpdateR, 0, mark, width - 1, row);
  1710.                                 mark := row;
  1711.                                 UpdateScreen(UpdateR);
  1712.                             end;
  1713.                         SelectSlice(2);
  1714.                         PutLine(0, row, width, sLine);