home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / image144.sit / Functions.p < prev    next >
Encoding:
Text File  |  1992-03-30  |  44.0 KB  |  1,729 lines

  1. unit Functions;
  2.  
  3. {}
  4.  
  5. interface
  6.  
  7.  
  8.     uses
  9.         QuickDraw, Palettes, Picker, PrintTraps, globals, Utilities, Graphics, File1, File2, Analysis, Camera, Lut;
  10.  
  11.  
  12.     procedure ApplyTable (var table: LookupTable);
  13.     procedure ApplyLookupTable;
  14.     procedure MakeBinary;
  15.     procedure Filter (ftype: FilterType; pass: integer; var table: FateTable);
  16.     procedure PhotoMode;
  17.     function AllSameSize: boolean;
  18.     procedure EnhanceContrast;
  19.     procedure EqualizeHistogram;
  20.     procedure Convolve (name: str255; RefNum: integer);
  21.     procedure PlotSurface;
  22.     procedure MakeSkeleton;
  23.     procedure DoErosion;
  24.     procedure DoDilation;
  25.     procedure DoOpening;
  26.     procedure DoClosing;
  27.     procedure SetBinaryCount;
  28.     procedure SetIterations;
  29.     procedure ChangeValues (v1, v2, v3: integer);
  30.     procedure DoPropagate (MenuItem: integer);
  31.     procedure DoArithmetic (MenuItem: integer; constant: extended);
  32.     procedure SortPalette (item: integer);
  33.     procedure NewPlotSurface;
  34.  
  35.  
  36. implementation
  37.  
  38.     const
  39.         MaxW = 4000;
  40.  
  41.     type
  42.         ktype = array[0..MaxW] of integer;
  43.  
  44.     var
  45.         PixelsRemoved: LongInt;
  46.  
  47.     procedure ApplyTableToLine (data: ptr; var table: LookupTable; width: LongInt);
  48. {$IFC false}
  49.         var
  50.             line: LinePtr;
  51.             i: integer;
  52.     begin
  53.         line := LinePtr(data);
  54.         for i := 0 to width - 1 do
  55.             Line^[i] := table[Line^[i]];
  56.     end;
  57. {$ENDC}
  58.  
  59. {a0 = data}
  60. {a1 = lookup table}
  61. {d0 = width }
  62. {d1 = pixel value}
  63. inline
  64.     $4E56, $0000, {  link a6,#0}
  65.     $48E7, $C0C0, {  movem.l a0-a1/d0-d1,-(sp)}
  66.     $206E, $000C, {  move.l 12(a6),a0}
  67.     $226E, $0008, {  move.l 8(a6),a1}
  68.     $202E, $0004, {  move.l 4(a6),d0}
  69.     $5380,       {  subq.l #1,d0}
  70.     $4281,       {  clr.l d1}
  71.     $1210,       {L move.b (a0),d1}
  72.     $10F1, $1000, {  move.b 0(a1,d1.w),(a0)+}
  73.     $51C8, $FFF8, {  dbra d0,L}
  74.     $4CDF, $0303, {  movem.l (sp)+,a0-a1/d0-d1}
  75.     $4E5E,       {  unlk a6}
  76.     $DEFC, $000C; {  add.w #12,sp}
  77.  
  78.  
  79. procedure PutLineUsingMask (h, v, count: integer; var line: LineType);
  80.     var
  81.         aLine, MaskLine: LineType;
  82.         i: integer;
  83.         SaveInfo: InfoPtr;
  84. begin
  85.     if count > MaxLine then
  86.         count := MaxLine;
  87.     GetLine(h, v, count, aline);
  88.     SaveInfo := Info;
  89.     Info := UndoInfo;
  90.     GetLine(h, v, count, MaskLine);
  91.     for i := 0 to count - 1 do
  92.         if MaskLine[i] = BlackIndex then
  93.             aLine[i] := line[i];
  94.     info := SaveInfo;
  95.     PutLine(h, v, count, aLine);
  96. end;
  97.  
  98.  
  99. procedure ApplyTable; {(var table: LookupTable)}
  100.     var
  101.         width, NumberOfLines, i, hloc, vloc: integer;
  102.         offset: LongInt;
  103.         p: ptr;
  104.         UseMask: boolean;
  105.         TempLine: LineType;
  106.         AutoSelectAll: boolean;
  107. begin
  108.     if NotInBounds then
  109.         exit(ApplyTable);
  110.     StopDigitizing;
  111.     AutoSelectAll := not Info^.RoiShowing;
  112.     if AutoSelectAll then
  113.         SelectAll(false);
  114.     if TooWide then
  115.         exit(ApplyTable);
  116.     ShowWatch;
  117.     with info^.RoiRect, info^ do begin
  118.             if RoiType <> RectRoi then
  119.                 UseMask := SetupMask
  120.             else
  121.                 UseMask := false;
  122.             SetupUndoFromClip;
  123.             WhatToUndo := UndoTransform;
  124.             offset := LongInt(top) * BytesPerRow + left;
  125.             if UseMask then
  126.                 p := @TempLine
  127.             else
  128.                 p := ptr(ord4(PicBaseAddr) + offset);
  129.             width := right - left;
  130.             NumberOfLines := bottom - top;
  131.             hloc := left;
  132.             vloc := top;
  133.         end;
  134.     if width > 0 then
  135.         for i := 1 to NumberOfLines do
  136.             if UseMask then begin
  137.                     GetLine(hloc, vloc, width, TempLine);
  138.                     ApplyTableToLine(p, table, width);
  139.                     PutLineUsingMask(hloc, vloc, width, TempLine);
  140.                     vloc := vloc + 1
  141.                 end
  142.             else begin
  143.                     ApplyTableToLine(p, table, width);
  144.                     p := ptr(ord4(p) + info^.BytesPerRow);
  145.                 end;
  146.     with info^ do begin
  147.             UpdateScreen(RoiRect);
  148.             Info^.changes := true;
  149.         end;
  150.     SetupRoiRect;
  151.     if AutoSelectAll then
  152.         KillRoi;
  153. end;
  154.  
  155.  
  156. function DoApplyTableDialogBox: boolean;
  157.     const
  158.         Button1 = 3;
  159.         Button2 = 4;
  160.         Button3 = 5;
  161.         Button4 = 6;
  162.     var
  163.         mylog: DialogPtr;
  164.         item: integer;
  165.         SaveA, SaveB: boolean;
  166.  
  167.     procedure SetButtons;
  168.     begin
  169.         SetDialogItem(mylog, Button1, ord(ThresholdToForeground));
  170.         SetDialogItem(mylog, Button2, ord(not ThresholdToForeground));
  171.         SetDialogItem(mylog, Button3, ord(NonThresholdToBackground));
  172.         SetDialogItem(mylog, Button4, ord(not NonThresholdToBackground));
  173.     end;
  174.  
  175. begin
  176.     InitCursor;
  177.     SaveA := ThresholdToForeground;
  178.     SaveB := NonThresholdToBackground;
  179.     mylog := GetNewDialog(40, nil, pointer(-1));
  180.     SetButtons;
  181.     OutlineButton(MyLog, ok, 16);
  182.     repeat
  183.         ModalDialog(nil, item);
  184.         if (item = Button1) or (item = button2) then begin
  185.                 ThresholdToForeground := not ThresholdToForeground;
  186.                 SetButtons;
  187.             end;
  188.         if (item = Button3) or (item = button4) then begin
  189.                 NonThresholdToBackground := not NonThresholdToBackground;
  190.                 SetButtons;
  191.             end;
  192.     until (item = ok) or (item = cancel);
  193.     DisposDialog(mylog);
  194.     if item = cancel then begin
  195.             ThresholdToForeground := SaveA;
  196.             NonThresholdToBackground := SaveB;
  197.             DoApplyTableDialogBox := false
  198.         end
  199.     else
  200.         DoApplyTableDialogBox := true;
  201. end;
  202.  
  203.  
  204. procedure ApplyLookupTable;
  205.     var
  206.         table: LookupTable;
  207.         ConvertingColorPic, GrayScaleImage: boolean;
  208.         i: integer;
  209. begin
  210.     with info^ do begin
  211.             GrayScaleImage := (LUTMode = Grayscale) or (LUTMode = CustomGrayscale);
  212.             ConvertingColorPic := not GrayScaleImage and not DensitySlicing;
  213.             if ConvertingColorPic then
  214.                 KillRoi;
  215.             if DensitySlicing and (not macro) then begin
  216.                     if not DoApplyTableDialogBox then
  217.                         exit(ApplyLookupTable);
  218.                 end;
  219.             if thresholding then
  220.                 BinaryPic := true;
  221.             GetLookupTable(table);
  222.             if GrayscaleImage or ConvertingColorPic then
  223.                 ResetGrayMap;
  224.             ApplyTable(table);
  225.             if ConvertingColorPic then
  226.                 WhatToUndo := NothingToUndo;
  227.             if DensityCalibrated then begin
  228.                     DensityCalibrated := false;
  229.                     for i := 0 to 255 do
  230.                         cvalue[i] := i;
  231.                 end;
  232.         end; {with}
  233. end;
  234.  
  235.  
  236. procedure MakeBinary;
  237.     var
  238.         table: LookupTable;
  239.         SaveBackground, SaveForeground: integer;
  240. begin
  241.     if not DensitySlicing and not Thresholding then
  242.         PutMessage('Sorry, but you must be thresholding or density slicing to use Make Binary.')
  243.     else begin
  244.             ThresholdToForeground := true;
  245.             NonThresholdToBackground := true;
  246.             SaveBackground := BackgroundIndex;
  247.             SaveForeground := ForegroundIndex;
  248.             BackgroundIndex := WhiteIndex;
  249.             ForegroundIndex := BlackIndex;
  250.             GetLookupTable(table);
  251.             ResetGrayMap;
  252.             ApplyTable(table);
  253.             BackgroundIndex := SaveBackground;
  254.             ForegroundIndex := SaveForeground;
  255.             info^.BinaryPic := true;
  256.         end;
  257. end;
  258.  
  259.  
  260. procedure Filter (ftype: FilterType; pass: integer; var table: FateTable);
  261.     const
  262.         PixelsPerUpdate = 5000;
  263.     var
  264.         row, width, r1, r2, r3, c, value, error, sum, center: integer;
  265.         tmp, mark, NewMark, LinesPerUpdate, LineCount: integer;
  266.         t1, t2, t3, t4: integer;
  267.         MaskRect, frame, trect: rect;
  268.         L1, L2, L3, result: LineType;
  269.         pt: point;
  270.         a: SortArray;
  271.         AutoSelectAll, UseMask: boolean;
  272.         L, T, R, B, index, code: integer;
  273.         StartTicks: LongInt;
  274. begin
  275.     if NotinBounds then
  276.         exit(Filter);
  277.     StopDigitizing;
  278.     AutoSelectAll := not Info^.RoiShowing;
  279.     if AutoSelectAll then
  280.         with info^ do begin
  281.                 SelectAll(false);
  282.                 SetPort(wptr);
  283.                 PenNormal;
  284.                 PenPat(pat[PatIndex]);
  285.                 FrameRect(wrect);
  286.             end;
  287.     if TooWide then
  288.         exit(Filter);
  289.     ShowWatch;
  290.     if info^.RoiType <> RectRoi then
  291.         UseMask := SetupMask
  292.     else
  293.         UseMask := false;
  294.     if pass = 0 then begin
  295.             SetupUndoFromClip;
  296.             ShowMessage(CmdPeriodToStop);
  297.             WhatToUndo := UndoFilter;
  298.         end;
  299.     frame := info^.RoiRect;
  300.     StartTicks := TickCount;
  301.     with frame, Info^ do begin
  302.             changes := true;
  303.             RoiShowing := false;
  304.             if left > 0 then
  305.                 left := left - 1;
  306.             if right < PicRect.right then
  307.                 right := right + 1;
  308.             width := right - left;
  309.             LinesPerUpdate := PixelsPerUpdate div width;
  310.             if ftype = ReduceNoise then
  311.                 LinesPerUpdate := LinesPerUpdate div 3;
  312.             GetLine(left, top, width, L2);
  313.             GetLine(left, top + 1, width, L3);
  314.             Mark := RoiRect.top;
  315.             LineCount := 0;
  316.             for row := top + 1 to bottom - 1 do begin
  317.        {Move Convolution Window Down}
  318.                     BlockMove(@L2, @L1, width);
  319.                     BlockMove(@L3, @L2, width);
  320.                     GetLine(left, row + 1, width, L3);
  321.        {Process One Row}
  322.                     case ftype of
  323.                         EdgeDetect: 
  324.                             for c := 1 to width - 2 do begin
  325.                                     t1 := L1[c] + L1[c + 1] + L1[c + 2] - L3[c] - L3[c + 1] - L3[c + 2];
  326.                                     t1 := abs(t1);
  327.                                     t2 := L1[c + 2] + L2[c + 2] + L3[c + 2] - L1[c] - L2[c] - L3[c];
  328.                                     t2 := abs(t2);
  329.                                     if t1 > t2 then
  330.                                         tmp := t1
  331.                                     else
  332.                                         tmp := t2;
  333.                                     if OptionKeyWasDown then begin
  334.                                             if tmp > 255 then
  335.                                                 tmp := 255;
  336.                                             if tmp < 0 then
  337.                                                 tmp := 0;
  338.                                         end
  339.                                     else if tmp > 35 then
  340.                                         tmp := 255
  341.                                     else
  342.                                         tmp := 0;
  343.                                     result[c - 1] := tmp;
  344.                                 end;
  345.                         ReduceNoise:  {Median Filter}
  346.                             for c := 1 to width - 2 do begin
  347.                                     a[1] := L1[c];
  348.                                     a[2] := L1[c + 1];
  349.                                     a[3] := L1[c + 2];
  350.                                     a[4] := L2[c];
  351.                                     a[5] := L2[c + 1];
  352.                                     a[6] := L2[c + 2];
  353.                                     a[7] := L3[c];
  354.                                     a[8] := L3[c + 1];
  355.                                     a[9] := L3[c + 2];
  356.                                     result[c - 1] := FindMedian(a);
  357.                                 end;
  358.                         Dither:  {Floyd-Steinberg Algorithm}
  359.                             for c := 1 to width - 2 do begin
  360.                                     value := L2[c + 1];
  361.                                     if value < 128 then begin
  362.                                             result[c - 1] := 0;
  363.                                             error := -value;
  364.                                         end
  365.                                     else begin
  366.                                             result[c - 1] := 255;
  367.                                             error := 255 - value
  368.                                         end;
  369.                                     tmp := L2[c + 2];              {A}
  370.                                     tmp := tmp - (7 * error) div 16;
  371.                                     if tmp < 0 then
  372.                                         tmp := 0;
  373.                                     if tmp > 255 then
  374.                                         tmp := 255;
  375.                                     L2[c + 2] := tmp;
  376.                                     tmp := L3[c + 2];              {B}
  377.                                     tmp := tmp - error div 16;
  378.                                     if tmp < 0 then
  379.                                         tmp := 0;
  380.                                     if tmp > 255 then
  381.                                         tmp := 255;
  382.                                     L3[c + 2] := tmp;
  383.                                     tmp := L3[c + 1];              {C}
  384.                                     tmp := tmp - (5 * error) div 16;
  385.                                     if tmp < 0 then
  386.                                         tmp := 0;
  387.                                     if tmp > 255 then
  388.                                         tmp := 255;
  389.                                     L3[c + 1] := tmp;
  390.                                     tmp := L3[c];                {D}
  391.                                     tmp := tmp - (3 * error) div 16;
  392.                                     if tmp < 0 then
  393.                                         tmp := 0;
  394.                                     if tmp > 255 then
  395.                                         tmp := 255;
  396.                                     L3[c] := tmp;
  397.                                 end;
  398.                         UnweightedAvg: 
  399.                             for c := 1 to width - 2 do begin
  400.                                     tmp := (L1[c] + L1[c + 1] + L1[c + 2] + L2[c] + L2[c + 1] + L2[c + 2] + L3[c] + L3[c + 1] + L3[c + 2]) div 9;
  401.                                     if tmp > 255 then
  402.                                         tmp := 255;
  403.                                     if tmp < 0 then
  404.                                         tmp := 0;
  405.                                     result[c - 1] := tmp;
  406.                                 end;
  407.                         WeightedAvg: 
  408.                             for c := 1 to width - 2 do begin
  409.                                     tmp := (L1[c] + L1[c + 1] + L1[c + 2] + L2[c] + L2[c + 1] * 4 + L2[c + 2] + L3[c] + L3[c + 1] + L3[c + 2]) div 12;
  410.                                     if tmp > 255 then
  411.                                         tmp := 255;
  412.                                     if tmp < 0 then
  413.                                         tmp := 0;
  414.                                     result[c - 1] := tmp;
  415.                                 end;
  416.                         fsharpen: 
  417.                             for c := 1 to width - 2 do begin
  418.                                     if OptionKeyWasDown then
  419.                                         tmp := L2[c + 1] * 9 - L1[c] - L1[c + 1] - L1[c + 2] - L2[c] - L2[c + 2] - L3[c] - L3[c + 1] - L3[c + 2]
  420.                                     else begin
  421.                                             tmp := L2[c + 1] * 12 - L1[c] - L1[c + 1] - L1[c + 2] - L2[c] - L2[c + 2] - L3[c] - L3[c + 1] - L3[c + 2];
  422.                                             tmp := tmp div 4;
  423.                                         end;
  424.                                     if tmp > 255 then
  425.                                         tmp := 255;
  426.                                     if tmp < 0 then
  427.                                         tmp := 0;
  428.                                     result[c - 1] := tmp;
  429.                                 end;
  430.                         fshadow: 
  431.                             for c := 1 to width - 2 do begin
  432.                                     tmp := L2[C + 1] + L2[C + 2] + L3[C + 1] + L3[C + 2] * 2 - L1[C] * 2 - L1[C + 1] - L2[C];
  433.                                     if tmp > 255 then
  434.                                         tmp := 255;
  435.                                     if tmp < 0 then
  436.                                         tmp := 0;
  437.                                     result[c - 1] := tmp;
  438.                                 end;
  439.                         Erosion: 
  440.                             for c := 1 to width - 2 do begin
  441.                                     center := L2[c + 1];
  442.                                     if center = BlackIndex then begin
  443.                                             sum := L1[c] + L1[c + 1] + L1[c + 2] + L2[c] + L2[c + 2] + L3[c] + L3[c + 1] + L3[c + 2];
  444.                                             if (2040 - sum) >= BinaryThreshold then
  445.                                                 center := WhiteIndex;
  446.                                         end;
  447.                                     result[c - 1] := center;
  448.                                 end;
  449.                         Dilation: 
  450.                             for c := 1 to width - 2 do begin
  451.                                     center := L2[c + 1];
  452.                                     if center = WhiteIndex then begin
  453.                                             sum := L1[c] + L1[c + 1] + L1[c + 2] + L2[c] + L2[c + 2] + L3[c] + L3[c + 1] + L3[c + 2];
  454.                                             if sum >= BinaryThreshold then
  455.                                                 center := BlackIndex;
  456.                                         end;
  457.                                     result[c - 1] := center;
  458.                                 end;
  459.                         OutlineFilter: 
  460.                             for c := 1 to width - 2 do begin
  461.                                     center := L2[c + 1];
  462.                                     if center = BlackIndex then begin
  463.                                             if (L2[c] = WhiteIndex) or (L1[c + 1] = WhiteIndex) or (L2[c + 2] = WhiteIndex) or (L3[c + 1] = WhiteIndex) then
  464.                                                 center := BlackIndex
  465.                                             else
  466.                                                 center := WhiteIndex;
  467.                                         end;
  468.                                     result[c - 1] := center;
  469.                                 end;
  470.  
  471.                         Skeletonize: 
  472.                             for c := 1 to width - 2 do begin
  473.                                     center := L2[c + 1];
  474.                                     if center = BlackIndex then begin
  475.                                             index := 0;
  476.                                             if L1[c] = BlackIndex then
  477.                                                 index := bor(index, 1);
  478.                                             if L1[c + 1] = BlackIndex then
  479.                                                 index := bor(index, 2);
  480.                                             if L1[c + 2] = BlackIndex then
  481.                                                 index := bor(index, 4);
  482.                                             if L2[c + 2] = BlackIndex then
  483.                                                 index := bor(index, 8);
  484.                                             if L3[c + 2] = BlackIndex then
  485.                                                 index := bor(index, 16);
  486.                                             if L3[c + 1] = BlackIndex then
  487.                                                 index := bor(index, 32);
  488.                                             if L3[c] = BlackIndex then
  489.                                                 index := bor(index, 64);
  490.                                             if L2[c] = BlackIndex then
  491.                                                 index := bor(index, 128);
  492.                                             code := table[index];
  493.                                             if odd(pass) then begin
  494.                                                     if (code = 2) or (code = 3) then begin
  495.                                                             center := WhiteIndex;
  496.                                                             PixelsRemoved := PixelsRemoved + 1;
  497.                                                         end;
  498.                                                 end
  499.                                             else begin {even pass}
  500.                                                     if (code = 1) or (code = 3) then begin
  501.                                                             center := WhiteIndex;
  502.                                                             PixelsRemoved := PixelsRemoved + 1;
  503.                                                         end;
  504.                                                 end;
  505.                                         end; {if}
  506.                                     result[c - 1] := center;
  507.                                 end; {for}
  508.                     end; {case}
  509.                     if UseMask then
  510.                         PutLineUsingMask(left + 2, row, width - 3, result)
  511.                     else
  512.                         PutLine(left + 2, row, width - 3, result);
  513.                     LineCount := LineCount + 1;
  514.                     if LineCount = LinesPerUpdate then begin
  515.                             pt.h := RoiRect.left;
  516.                             pt.v := row + 1;
  517.                             NewMark := pt.v;
  518.                             with RoiRect do
  519.                                 SetRect(MaskRect, left, mark, right, NewMark);
  520.                             UpdateScreen(MaskRect);
  521.                             LineCount := 0;
  522.                             Mark := NewMark;
  523.                             if magnification > 1.0 then
  524.                                 Mark := Mark - 1;
  525.                             if CommandPeriod then begin
  526.                                     UpdatePicWindow;
  527.                                     beep;
  528.                                     PixelsRemoved := 0;
  529.                                     if AutoSelectAll then
  530.                                         KillRoi;
  531.                                     exit(filter)
  532.                                 end;
  533.                         end;
  534.                 end; {for row:=...}
  535.             trect := frame;
  536.             InsetRect(trect, 1, 1);
  537.             ShowTime(StartTicks, trect, '');
  538.         end; {with}
  539.     if LineCount > 0 then begin
  540.             with frame do
  541.                 SetRect(MaskRect, left, mark, right, bottom);
  542.             UpdateScreen(MaskRect)
  543.         end;
  544.     SetupRoiRect;
  545.     if AutoSelectAll then
  546.         KillRoi;
  547. end;
  548.  
  549.  
  550. procedure PhotoMode;
  551. {Erases the screen to the background color and then redraws}
  552. {the contents of the active image window . }
  553.     var
  554.         tPort: GrafPtr;
  555.         event: EventRecord;
  556.         WinRect: rect;
  557.         SaveVisRgn: rgnHandle;
  558. begin
  559.     if info <> NoInfo then
  560.         with info^ do begin
  561.                 KillRoi;
  562.                 if OptionKeyWasDown then begin {Move window up to top of screen.}
  563.                         GetWindowRect(wptr, WinRect);
  564.                         MoveWindow(wptr, WinRect.left, 0, false);
  565.                     end;
  566.                 with wptr^ do begin
  567.                         SaveVisRgn := visRgn;
  568.                         visRgn := NewRgn;
  569.                         RectRgn(visRgn, ScreenBits.Bounds);
  570.                     end;
  571.                 FlushEvents(EveryEvent, 0);
  572.                 GetPort(tPort);
  573.                 EraseScreen;
  574.                 UpdatePicWindow;
  575.                 repeat
  576.                 until WaitNextEvent(mDownMask + KeyDownMask, Event, 5, nil);
  577.                 with wptr^ do begin
  578.                         DisposeRgn(visRgn);
  579.                         visRgn := SaveVisRgn;
  580.                     end;
  581.                 RestoreScreen;
  582.                 SetPort(tPort);
  583.                 FlushEvents(EveryEvent, 0);
  584.                 if OptionKeyWasDown then begin
  585.                         MoveWindow(wptr, WinRect.left, WinRect.top, false);
  586.                     end;
  587.             end
  588.     else
  589.         beep;
  590. end;
  591.  
  592.  
  593. function AllSameSize: boolean;
  594. {Returns true if all currently open Images have the same dimensions.}
  595.     var
  596.         i: integer;
  597.         SameSize: Boolean;
  598.         TempInfo: InfoPtr;
  599. begin
  600.     if nPics = 0 then begin
  601.             AllSameSize := false;
  602.             exit(AllSameSize);
  603.         end;
  604.     SameSize := true;
  605.     for i := 1 to nPics do begin
  606.             TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
  607.             SameSize := SameSize and EqualRect(Info^.PicRect, TempInfo^.PicRect);
  608.         end;
  609.     AllSameSize := SameSize;
  610. end;
  611.  
  612.  
  613. procedure EnhanceContrast;
  614.     var
  615.         AutoSelectAll: boolean;
  616.         min, max, i, threshold: integer;
  617.         found: boolean;
  618.         sum: LongInt;
  619. begin
  620.     with info^ do
  621.         if (LUTMode <> GrayScale) and (LUTMode <> CustomGrayscale) then begin
  622.                 PutMessage('Sorry, but you can only contrast enhance grayscale images.');
  623.                 exit(EnhanceContrast)
  624.             end;
  625.     if NotInBounds or (ClipBuf = nil) then
  626.         exit(EnhanceContrast);
  627.     StopDigitizing;
  628.     AutoSelectAll := not Info^.RoiShowing;
  629.     if AutoSelectAll then
  630.         SelectAll(false);
  631.     if info^.RoiType = RectRoi then
  632.         GetRectHistogram
  633.     else
  634.         GetNonRectHistogram;
  635.     sum := 0;
  636.     for i := 0 to 255 do
  637.         sum := sum + histogram[i];
  638.     threshold := sum div 5000;
  639.     i := -1;
  640.     repeat
  641.         i := i + 1;
  642.         found := histogram[i] > threshold;
  643.     until found or (i = 255);
  644.     min := i;
  645.     i := 256;
  646.     repeat
  647.         i := i - 1;
  648.         found := histogram[i] > threshold;
  649.     until found or (i = 0);
  650.     max := i;
  651.     if max > min then
  652.         with info^ do begin
  653.                 SetupLutUndo;
  654.                 ColorStart := min;
  655.                 ColorEnd := max;
  656.                 DrawMap;
  657.                 UpdateLUT;
  658.                 changes := true;
  659.                 IdentityFunction := false;
  660.             end;
  661.     if AutoSelectAll then
  662.         KillRoi;
  663. end;
  664.  
  665.  
  666. procedure EqualizeHistogram;
  667.     var
  668.         AutoSelectAll: boolean;
  669.         i, sum, v: integer;
  670.         isum: LongInt;
  671.         ScaleFactor: extended;
  672. begin
  673.     with info^ do
  674.         if (LUTMode <> GrayScale) and (LutMode <> CustomGrayscale) then begin
  675.                 PutMessage('Sorry, but you can only do histogram equalization on grayscale images.');
  676.                 exit(EqualizeHistogram)
  677.             end;
  678.     if NotInBounds or (ClipBuf = nil) then
  679.         exit(EqualizeHistogram);
  680.     StopDigitizing;
  681.     AutoSelectAll := not Info^.RoiShowing;
  682.     if AutoSelectAll then
  683.         SelectAll(false);
  684.     if info^.RoiType = RectRoi then
  685.         GetRectHistogram
  686.     else
  687.         GetNonRectHistogram;
  688.     FindThresholdingMode;
  689.     ComputeResults;
  690.     isum := 0;
  691.     for i := 0 to 255 do
  692.         isum := isum + histogram[i];
  693.     ScaleFactor := 255.0 / isum;
  694.     sum := 0;
  695.     with info^ do begin
  696.             SetupLutUndo;
  697.             for i := 255 downto 0 do
  698.                 with cTable[i].rgb do begin
  699.                         sum := round(sum + histogram[i] * ScaleFactor);
  700.                         if sum > 255 then
  701.                             sum := 255;
  702.                         v := sum * 256;
  703.                         red := v;
  704.                         green := v;
  705.                         blue := v;
  706.                     end;
  707.             LoadLUT(cTable);
  708.             LUTMode := CustomGrayscale;
  709.             SetupPseudocolor;
  710.             changes := true;
  711.             DrawMap;
  712.             IdentityFunction := false;
  713.         end; {with info}
  714.     if AutoSelectAll then
  715.         KillRoi;
  716. end;
  717.  
  718.  
  719. procedure GetKernel (var kernel: ktype; var n: integer; var name: str255; RefNum: integer);
  720.     var
  721.         rLine: rLineType;
  722.         i, count, nValues, nRows: integer;
  723. begin
  724.     count := 0;
  725.     nRows := 0;
  726.     InitTextInput(name, RefNum);
  727.     while not TextEof and (nRows <= 63) do begin
  728.             GetLineFromText(rLine, nValues);
  729.             if count <> 0 then
  730.                 nRows := nRows + 1;
  731.             if nRows = 1 then
  732.                 n := nValues;
  733.             for i := 1 to nValues do begin
  734.                     count := count + 1;
  735.                     kernel[count - 1] := round(rLine[i]);
  736.                 end;
  737.         end;
  738.     if count <> (n * n) then
  739.         n := 0;
  740. end;
  741.  
  742.  
  743. procedure DoOnePixel (nLess1, BytesPerLine: integer; corner: LongInt; var sum: LongInt; var kernel: ktype);
  744. {$IFC false}
  745.     var
  746.         row, column, k: integer;
  747.         pp: ptr;
  748. begin
  749.     k := 0;
  750.     sum := 0;
  751.     for row := 0 to nless1 do begin
  752.             corner := corner + BytesPerLine;
  753.             pp := ptr(corner);
  754.             for column := 0 to nless1 do begin
  755.                     sum := sum + band(pp^, 255) * kernel[k];
  756.                     k := k + 1;
  757.                     pp := ptr(ord(pp) + 1);
  758.                 end;
  759.         end;
  760. end;
  761. {$ENDC}
  762.  
  763. {a0=^corner/^sum}
  764. {a1=^kernel}
  765. {a2=^pixels}
  766.  
  767. {d0=n-1}
  768. {d1=BytesPerLine}
  769. {d2=sum}
  770. {d3=n-1(outer loop)}
  771. {d4=n-1(inner loop)}
  772. {d5=temp}
  773.  
  774. inline
  775.     $4E56, $0000, {  link    a6,#0}
  776.     $48E7, $FCE0,  {  movem.l    a0-a2/d0-d5,-(sp)}
  777.     $4280,              {  clr.l    d0}
  778.     $302E, $0012, {  move.w    18(a6),d0}
  779.     $4281,              {  clr.l    d1}
  780.     $322E, $0010, {  move.w    16(a6),d1}
  781.     $206E, $000C, {  movea.l    12(a6),a0}
  782.     $226E, $0004, {  movea.l    4(a6),a1}
  783.  
  784.     $4282,             {  clr.l    d2}
  785.     $2600,             {  move.l    d0,d3}
  786.  
  787.     $D1C1,             {A adda.l    d1,a0}
  788.     $2448,            {  move.l    a0,a2}
  789.     $2800,            {  move.l    d0,d4}
  790.     $4285,            {B clr.l    d5                   (2)}
  791.     $1A1A,             {  move.b    (a2)+,d5    (6) }
  792.     $CBD9,             {  muls    (a1)+,d5     (29!)}
  793.     $D485,             {  add.l    d5,d2          (2)}
  794.     $51CC, $FFF6, {  dbra    d4,B                (6)}
  795.     $51CB, $FFEC, {  dbra    d3,A}
  796.  
  797.     $206E, $0008, {  move.l    8(a6),a0}
  798.     $2082,              {  move.l    d2,(a0)}
  799.     $4CDF, $073F, {  movem.l    (sp)+,a0-a2/d0-d5}
  800.     $4E5E,              {  unlk    a6}
  801.     $DEFC, $0010; {  add.w    #16,sp}
  802.  
  803.  
  804.  
  805. procedure DoConvolution (var kernel: ktype; n: integer);
  806.     const
  807.         skip = 7;
  808.     var
  809.         row, width, column, value, error: integer;
  810.         margin, i, nless1: integer;
  811.         frame, MaskRect, tRect: rect;
  812.         AutoSelectAll, ScalingNeeded: boolean;
  813.         SrcCenter, DstCenter, sum, max, offset, wsum, cscale, StartTicks: LongInt;
  814.         MinResult, MaxResult: LongInt;
  815.         p: ptr;
  816.         str, str2: str255;
  817.         ScaleFactor: extended;
  818. begin
  819.     if NotinBounds or NotRectangular then
  820.         exit(DoConvolution);
  821.     StopDigitizing;
  822.     AutoSelectAll := not Info^.RoiShowing;
  823.     if AutoSelectAll then
  824.         SelectAll(false);
  825.     SetupUndoFromClip;
  826.     WhatToUndo := UndoFilter;
  827.     frame := info^.RoiRect;
  828.     with frame, Info^ do begin
  829.             if ((LutMode = GrayScale) or (LutMode = CustomGrayscale)) and (not IdentityFunction) then
  830.                 ApplyLookupTable;
  831.             changes := true;
  832.             margin := n div 2;
  833.             if left < margin then
  834.                 left := left + margin;
  835.             if right > (PicRect.right - margin) then
  836.                 right := right - margin;
  837.             if top < margin then
  838.                 top := top + margin;
  839.             if bottom > (PicRect.bottom - margin) then
  840.                 bottom := bottom - margin;
  841.             SetPort(wptr);
  842.             PenNormal;
  843.             PenPat(pat[PatIndex]);
  844.             tRect := frame;
  845.             OffscreenToScreenRect(tRect);
  846.             FrameRect(tRect);
  847.             width := right - left;
  848.             max := n * n - 1;
  849.             wsum := 0;
  850.             for i := 0 to max do
  851.                 wsum := wsum + kernel[i];
  852.             NumToString(n, str);
  853.             NumToString(wsum, str2);
  854.             ValuesMessage := Concat(str, ' x ', str, ' kernel', cr, 'sum = ', str2, cr, cr, CmdPeriodToStop);
  855.             ShowValues;
  856.             if wsum <> 0 then
  857.                 cscale := wsum
  858.             else
  859.                 cscale := 1;
  860.             offset := -(n div 2) * BytesPerRow - BytesPerRow - n div 2;
  861.             nless1 := n - 1;
  862.             StartTicks := TickCount;
  863.             str := '';
  864.             if ScaleConvolutions then begin
  865.                     MinResult := MaxLongInt;
  866.                     MaxResult := -MaxLongInt;
  867.                     row := top;
  868.                     while row < bottom do begin
  869.                             SrcCenter := ord4(ClipBufInfo^.PicBaseAddr) + LongInt(row) * BytesPerRow + left;
  870.                             column := left;
  871.                             while column < (left + width) do begin
  872.                                     DoOnePixel(nless1, BytesPerRow, SrcCenter + offset, sum, kernel);
  873.                                     value := sum div cscale;
  874.                                     if value < MinResult then
  875.                                         MinResult := value;
  876.                                     if value > MaxResult then
  877.                                         MaxResult := value;
  878.                                     SrcCenter := SrcCenter + skip;
  879.                                     column := column + skip;
  880.                                 end; {while column}
  881.                             row := row + skip;
  882.                         end; {while row...}
  883.                     ScalingNeeded := (MinResult < 0) or (MaxResult > 255);
  884.                     if ScalingNeeded then
  885.                         ScaleFactor := 253.0 / (MaxResult - MinResult)
  886.                     else
  887.                         ScaleFactor := 1.0;
  888.                     RealToString(ScaleFactor, 1, 4, str);
  889.                     str := concat('min=', long2str(MinResult), cr, 'max=', long2str(MaxResult), cr, 'scale factor= ', str);
  890.                     for row := top to bottom - 1 do begin
  891.                             SrcCenter := ord4(ClipBufInfo^.PicBaseAddr) + LongInt(row) * BytesPerRow + left;
  892.                             DstCenter := ord4(PicBaseAddr) + LongInt(row) * BytesPerRow + left;
  893.                             for column := left to left + width - 1 do begin
  894.                                     DoOnePixel(nless1, BytesPerRow, SrcCenter + offset, sum, kernel);
  895.                                     value := sum div cscale;
  896.                                     if ScalingNeeded then begin
  897.                                             if value < MinResult then
  898.                                                 value := MinResult;
  899.                                             if value > MaxResult then
  900.                                                 value := MaxResult;
  901.                                             value := round((value - MinResult) * ScaleFactor + 1);
  902.                                         end;
  903.                                     p := ptr(DstCenter);
  904.                                     p^ := BAND(value, 255);
  905.                                     SrcCenter := SrcCenter + 1;
  906.                                     DstCenter := DstCenter + 1;
  907.                                 end; {for column:=}
  908.                             SetRect(MaskRect, left, row, right, row + 1);
  909.                             UpdateScreen(MaskRect);
  910.                             if CommandPeriod then begin
  911.                                     UpdatePicWindow;
  912.                                     beep;
  913.                                     exit(DoConvolution)
  914.                                 end;
  915.                         end; {for row:=...}
  916.                 end  {Scale Convolutions}
  917.             else
  918.                 for row := top to bottom - 1 do begin
  919.                         SrcCenter := ord4(ClipBufInfo^.PicBaseAddr) + LongInt(row) * BytesPerRow + left;
  920.                         DstCenter := ord4(PicBaseAddr) + LongInt(row) * BytesPerRow + left;
  921.                         for column := left to left + width - 1 do begin
  922.                                 DoOnePixel(nless1, BytesPerRow, SrcCenter + offset, sum, kernel);
  923.                                 value := sum div cscale;
  924.                                 if value < MinResult then
  925.                                     MinResult := value;
  926.                                 if value > MaxResult then
  927.                                     MaxResult := value;
  928.                                 if value > 255 then
  929.                                     value := 255;
  930.                                 if value < 0 then
  931.                                     value := 0;
  932.                                 p := ptr(DstCenter);
  933.                                 p^ := BAND(value, 255);
  934.                                 SrcCenter := SrcCenter + 1;
  935.                                 DstCenter := DstCenter + 1;
  936.                             end; {for column:=}
  937.                         SetRect(MaskRect, left, row, right, row + 1);
  938.                         UpdateScreen(MaskRect);
  939.                         if CommandPeriod then begin
  940.                                 UpdatePicWindow;
  941.                                 beep;
  942.                                 exit(DoConvolution)
  943.                             end;
  944.                     end; {for row:=...}
  945.             ShowTime(StartTicks, frame, str);
  946.         end; {with}
  947.     UpdatePicWindow;
  948.     SetupRoiRect;
  949.     if AutoSelectAll then
  950.         KillRoi;
  951. end;
  952.  
  953.  
  954. procedure Convolve (name: str255; RefNum: integer);
  955.     var
  956.         kernel: ktype;
  957.         n, count: integer;
  958. begin
  959.     if name = '' then begin
  960.             if not OpenTextFile(name, RefNum) then
  961.                 exit(convolve)
  962.             else
  963.                 KernelsRefNum := RefNum;
  964.         end;
  965.     DisableDensitySlice;
  966.     GetKernel(kernel, n, name, RefNum);
  967.     count := n * n;
  968.     UpdatePicWindow;
  969.     if (n >= 3) and (n <= 63) then
  970.         DoConvolution(kernel, n)
  971.     else
  972.         PutMessage('Kernels must be n x n square matrices with 3 <= n <= 63.');
  973. end;
  974.  
  975.  
  976. procedure PlotSurface;
  977.     var
  978.         hend, vend, h, v, DataWidth, DataHeight, i: integer;
  979.         htemp, vtemp, ivalue: integer;
  980.         skip, DataLeft, DataRight, DataTop, DataBottom: integer;
  981.         hLoc, vLoc, hMin, hMax, vMin, vMax, MinIValue, MaxIValue: integer;
  982.         hstart, vstart, dh, dv, hbase, vbase, vscale, nPlotLines, CalValue: extended;
  983.         peak, MaxPeak, hinc, vinc, nLines, MinCValue, MaxCValue: extended;
  984.         poly: PolyHandle;
  985.         SaveInfo, PlotInfo: InfoPtr;
  986.         aLine: LineType;
  987.         MaskRect: rect;
  988.         AutoSelectAll, ApplyLUT: boolean;
  989.         table: LookupTable;
  990.         StartTicks: LongInt;
  991.  
  992.     procedure FindVinc;
  993.     begin
  994.         with PlotInfo^.PicRect do begin
  995.                 vstart := 5.0 + MaxPeak - dv * DataWidth;
  996.                 skip := round(DataHeight / ((bottom - vstart - 5.0) / vinc));
  997.                 if skip = 0 then
  998.                     skip := 1;
  999.                 nPlotLines := DataHeight / skip;
  1000.                 vinc := (bottom - vstart - 5.0) / nPlotLines;
  1001.                 vinc := vinc / 0.95;
  1002.                 repeat
  1003.                     vinc := vinc * 0.95;
  1004.                     hinc := vinc / 2.0;
  1005.                 until (5.0 + hinc * nPlotLines + dh * DataWidth) < right;
  1006.             end;
  1007.     end;
  1008.  
  1009. begin
  1010.     if NotRectangular or NotInBounds then
  1011.         exit(PlotSurface);
  1012.     StopDigitizing;
  1013.     DisableDensitySlice;
  1014.     SetForegroundColor(BlackIndex);
  1015.     SetBackgroundColor(WhiteIndex);
  1016.     SaveInfo := Info;
  1017.     if not NewPicWindow('Surface Plot', NewPicWidth, NewPicHeight) then begin
  1018.             KillRoi;
  1019.             exit(PlotSurface)
  1020.         end;
  1021.     PlotInfo := info;
  1022.     info := SaveInfo;
  1023.     AutoSelectAll := not Info^.RoiShowing;
  1024.     ShowWatch;
  1025.     if AutoSelectAll then
  1026.         SelectAll(true);
  1027.     if TooWide then
  1028.         exit(PlotSurface);
  1029.     with info^ do
  1030.         ApplyLUT := ((LutMode = GrayScale) or (LutMode = CustomGrayscale)) and (not IdentityFunction);
  1031.     if ApplyLUT then
  1032.         GetLookupTable(table);
  1033.     Measure;
  1034.     UndoLastMeasurement(true);
  1035.     with results do begin
  1036.             MinIValue := MinIndex;
  1037.             MaxIValue := MaxIndex;
  1038.         end;
  1039.     if ApplyLut then begin
  1040.             MinIvalue := table[MinIValue];
  1041.             MaxIvalue := table[MaxIValue];
  1042.         end;
  1043.     MinCValue := 10e100;
  1044.     MaxCValue := -10e100;
  1045.     for i := MinIValue to MaxIValue do begin
  1046.             ivalue := i;
  1047.             if ApplyLUT then
  1048.                 ivalue := table[ivalue];
  1049.             calValue := cvalue[i];
  1050.             if calValue < minCValue then
  1051.                 minCValue := calValue;
  1052.             if calValue > maxCValue then
  1053.                 maxCValue := calValue;
  1054.         end;
  1055.     WhatToUndo := NothingToUndo;
  1056.     with results do
  1057.         if (MaxValue - MinValue) <> 0.0 then
  1058.             vscale := (255.0 / (MaxValue - MinValue)) * 0.5
  1059.         else
  1060.             vscale := 0.5;
  1061.     with info^.RoiRect do begin
  1062.             DataLeft := left;
  1063.             DataRight := right;
  1064.             DataTop := top;
  1065.             DataBottom := bottom;
  1066.             DataWidth := DataRight - DataLeft;
  1067.             DataHeight := DataBottom - DataTop;
  1068.         end;
  1069.     dh := (0.65 * PlotInfo^.PicRect.right) / DataWidth;
  1070.     dv := -0.4 * dh;
  1071.     hstart := 5.0;
  1072.     vinc := 2.0;
  1073.     MaxPeak := (MaxCValue - MinCValue) * vscale * 0.5;
  1074.     FindVinc; {First estimate}
  1075.     MaxPeak := MaxPeak * 2.0;
  1076.     hmin := DataRight + round(MaxPeak / dv);
  1077.     if hmin < 0 then
  1078.         hmin := 0;
  1079.     vmax := DataTop + round(MaxPeak / vinc);
  1080.     if vmax > DataBottom then
  1081.         vmax := DataBottom;
  1082.     MaxPeak := 0.0;
  1083.     vloc := DataTop;
  1084.     skip := 3;
  1085.     repeat
  1086.         hloc := hmin;
  1087.         repeat
  1088.             ivalue := MyGetPixel(hloc, vloc);
  1089.             if ApplyLUT then
  1090.                 ivalue := table[ivalue];
  1091.             calValue := cvalue[ivalue];
  1092.             peak := (calValue - MinCValue) * vscale + (DataRight - hloc) * dv - (vloc - DataTop) * vinc;
  1093.             if peak > MaxPeak then
  1094.                 MaxPeak := peak;
  1095.             hloc := hloc + skip;
  1096.         until hloc > DataRight;
  1097.         vloc := vloc + skip;
  1098.     until vloc > vmax;
  1099.     FindVinc;
  1100.     v := DataTop;
  1101.     StartTicks := TickCount;
  1102.     SetPort(GrafPtr(PlotInfo^.osPort));
  1103.     PenNormal;
  1104.     repeat
  1105.         hmax := 0;
  1106.         vmin := 9999;
  1107.         poly := OpenPoly;
  1108.         hbase := hstart;
  1109.         vbase := vstart;
  1110.         Info := SaveInfo;
  1111.         GetLine(DataLeft, v, DataWidth, aLine);
  1112.         info := PlotInfo;
  1113.         if ApplyLUT then
  1114.             ApplyTableToLine(@aLine, table, DataWidth);
  1115.         MoveTo(round(hbase), round(vbase - vscale * (cvalue[aLine[0]] - MinCValue)));
  1116.         for i := 0 to DataWidth - 1 do begin
  1117.                 hbase := hbase + dh;
  1118.                 vbase := vbase + dv;
  1119.                 hLoc := round(hbase);
  1120.                 vLoc := round(vbase - vscale * (cvalue[aLine[i]] - MinCValue));
  1121.                 LineTo(hloc, vloc);
  1122.                 if hloc > hmax then
  1123.                     hmax := hloc;
  1124.                 if vloc < vmin then
  1125.                     vmin := vloc;
  1126.             end;
  1127.         LineTo(round(hbase), round(vbase));
  1128.         LineTo(round(hstart), round(vstart));
  1129.         LineTo(round(hstart), round(vstart - vscale * (cvalue[aLine[0]] - MinCValue)));
  1130.         hmin := round(hstart);
  1131.         vmax := round(vstart);
  1132.         ClosePoly;
  1133.         ErasePoly(poly);
  1134.         FramePoly(poly);
  1135.         KillPoly(poly);
  1136.         SetRect(MaskRect, hmin, vmin, hmax, vmax);
  1137.         UpdateScreen(MaskRect);
  1138.         hstart := hstart + hinc;
  1139.         vstart := vstart + vinc;
  1140.         v := v + skip;
  1141.     until (v >= DataBottom) or CommandPeriod;
  1142.     ShowTime(StartTicks, SaveInfo^.RoiRect, '');
  1143.     if CommandPeriod then
  1144.         beep;
  1145.     info^.changes := true;
  1146. end;
  1147.  
  1148.  
  1149. procedure NewPlotSurface;
  1150.     var
  1151.         hend, vend, h, v, DataWidth, DataHeight, i: integer;
  1152.         htemp, vtemp, ivalue, dh, dv, hbase, vbase: integer;
  1153.         skip, DataLeft, DataRight, DataTop, DataBottom: integer;
  1154.         hLoc, vLoc, hMin, hMax, vMin, vMax, MinIValue, MaxIValue: integer;
  1155.         hstart, vstart, vscale, nPlotLines, CalValue, edh, edv: extended;
  1156.         peak, MaxPeak, hinc, vinc, nLines, MinCValue, MaxCValue: extended;
  1157.         poly: PolyHandle;
  1158.         SaveInfo, PlotInfo: InfoPtr;
  1159.         aLine: LineType;
  1160.         MaskRect: rect;
  1161.         AutoSelectAll, ApplyLUT: boolean;
  1162.         table: LookupTable;
  1163.         StartTicks: LongInt;
  1164.  
  1165.     procedure FindVinc;
  1166.     begin
  1167.         with PlotInfo^.PicRect do begin
  1168.                 vstart := 5.0 + MaxPeak - edv * DataWidth;
  1169.                 skip := round(DataHeight / ((bottom - vstart - 5.0) / vinc));
  1170.                 if skip = 0 then
  1171.                     skip := 1;
  1172.                 nPlotLines := DataHeight / skip;
  1173.                 vinc := (bottom - vstart - 5.0) / nPlotLines;
  1174.                 vinc := vinc / 0.95;
  1175.                 repeat
  1176.                     vinc := vinc * 0.95;
  1177.                     hinc := vinc / 2.0;
  1178.                 until (5.0 + hinc * nPlotLines + edh * DataWidth) < right;
  1179.             end;
  1180.     end;
  1181.  
  1182. begin
  1183.     if NotRectangular or NotInBounds then
  1184.         exit(NewPlotSurface);
  1185.     StopDigitizing;
  1186.     DisableDensitySlice;
  1187.     SetForegroundColor(BlackIndex);
  1188.     SetBackgroundColor(WhiteIndex);
  1189.     SaveInfo := Info;
  1190.     if not NewPicWindow('Surface Plot', NewPicWidth, NewPicHeight) then begin
  1191.             KillRoi;
  1192.             exit(NewPlotSurface)
  1193.         end;
  1194.     PlotInfo := info;
  1195.     info := SaveInfo;
  1196.     AutoSelectAll := not Info^.RoiShowing;
  1197.     ShowWatch;
  1198.     if AutoSelectAll then
  1199.         SelectAll(true);
  1200.     if TooWide then
  1201.         exit(NewPlotSurface);
  1202.     with info^ do
  1203.         ApplyLUT := ((LutMode = GrayScale) or (LutMode = CustomGrayscale)) and (not IdentityFunction);
  1204.     if ApplyLUT then
  1205.         GetLookupTable(table);
  1206.     Measure;
  1207.     UndoLastMeasurement(true);
  1208.     with results do begin
  1209.             MinIValue := MinIndex;
  1210.             MaxIValue := MaxIndex;
  1211.         end;
  1212.     if ApplyLut then begin
  1213.             MinIvalue := table[MinIValue];
  1214.             MaxIvalue := table[MaxIValue];
  1215.         end;
  1216.     MinCValue := 10e100;
  1217.     MaxCValue := -10e100;
  1218.     for i := MinIValue to MaxIValue do begin
  1219.             ivalue := i;
  1220.             if ApplyLUT then
  1221.                 ivalue := table[ivalue];
  1222.             calValue := cvalue[i];
  1223.             if calValue < minCValue then
  1224.                 minCValue := calValue;
  1225.             if calValue > maxCValue then
  1226.                 maxCValue := calValue;
  1227.         end;
  1228.     WhatToUndo := NothingToUndo;
  1229.     with results do
  1230.         if (MaxValue - MinValue) <> 0.0 then
  1231.             vscale := (255.0 / (MaxValue - MinValue)) * 0.5
  1232.         else
  1233.             vscale := 0.5;
  1234.     with info^.RoiRect do begin
  1235.             DataLeft := left;
  1236.             DataRight := right;
  1237.             DataTop := top;
  1238.             DataBottom := bottom;
  1239.             DataWidth := DataRight - DataLeft;
  1240.             DataHeight := DataBottom - DataTop;
  1241.         end;
  1242.     edh := (0.65 * PlotInfo^.PicRect.right) / DataWidth;
  1243.     dh := round(edh);
  1244.     edv := -0.4 * edh;
  1245.     dv := round(edv);
  1246.     hstart := 5.0;
  1247.     vinc := 2.0;
  1248.     MaxPeak := (MaxCValue - MinCValue) * vscale * 0.5;
  1249.     FindVinc; {First estimate}
  1250.     MaxPeak := MaxPeak * 2.0;
  1251.     hmin := DataRight + round(MaxPeak / edv);
  1252.     if hmin < 0 then
  1253.         hmin := 0;
  1254.     vmax := DataTop + round(MaxPeak / vinc);
  1255.     if vmax > DataBottom then
  1256.         vmax := DataBottom;
  1257.     MaxPeak := 0.0;
  1258.     vloc := DataTop;
  1259.     skip := 3;
  1260.     repeat
  1261.         hloc := hmin;
  1262.         repeat
  1263.             ivalue := MyGetPixel(hloc, vloc);
  1264.             if ApplyLUT then
  1265.                 ivalue := table[ivalue];
  1266.             calValue := cvalue[ivalue];
  1267.             peak := (calValue - MinCValue) * vscale + LongInt(DataRight - hloc) * dv - (vloc - DataTop) * vinc;
  1268.             if peak > MaxPeak then
  1269.                 MaxPeak := peak;
  1270.             hloc := hloc + skip;
  1271.         until hloc > DataRight;
  1272.         vloc := vloc + skip;
  1273.     until vloc > vmax;
  1274.     FindVinc;
  1275.     v := DataTop;
  1276.     StartTicks := TickCount;
  1277.     SetPort(GrafPtr(PlotInfo^.osPort));
  1278.     PenNormal;
  1279.     repeat
  1280.         hmax := 0;
  1281.         vmin := 9999;
  1282.         poly := OpenPoly;
  1283.         hbase := round(hstart);
  1284.         vbase := round(vstart);
  1285.         Info := SaveInfo;
  1286.         GetLine(DataLeft, v, DataWidth, aLine);
  1287.         info := PlotInfo;
  1288.         if ApplyLUT then
  1289.             ApplyTableToLine(@aLine, table, DataWidth);
  1290.         MoveTo(hbase, vbase - round(vscale * (cvalue[aLine[0]] - MinCValue)));
  1291.         for i := 0 to DataWidth - 1 do begin
  1292.                 hbase := hbase + dh;
  1293.                 vbase := vbase + dv;
  1294.                 hLoc := hbase;
  1295.                 vLoc := vbase - round(vscale * (cvalue[aLine[i]] - MinCValue));
  1296.                 LineTo(hloc, vloc);
  1297.                 if hloc > hmax then
  1298.                     hmax := hloc;
  1299.                 if vloc < vmin then
  1300.                     vmin := vloc;
  1301.             end;
  1302.         LineTo(hbase, vbase);
  1303.         LineTo(round(hstart), round(vstart));
  1304.         LineTo(round(hstart), round(vstart - vscale * (cvalue[aLine[0]] - MinCValue)));
  1305.         hmin := round(hstart);
  1306.         vmax := round(vstart);
  1307.         ClosePoly;
  1308.         ErasePoly(poly);
  1309.         FramePoly(poly);
  1310.         KillPoly(poly);
  1311.         SetRect(MaskRect, hmin, vmin, hmax, vmax);
  1312.         UpdateScreen(MaskRect);
  1313.         hstart := hstart + hinc;
  1314.         vstart := vstart + round(vinc);
  1315.         v := v + skip;
  1316.     until (v >= DataBottom) or CommandPeriod;
  1317.     ShowTime(StartTicks, SaveInfo^.RoiRect, '');
  1318.     if CommandPeriod then
  1319.         beep;
  1320.     info^.changes := true;
  1321. end;
  1322.  
  1323.  
  1324. procedure MakeSkeleton;
  1325. {This table-driven parallel thinning routine is based on an algorithm}
  1326. {by Zhang and Suen(CACM, March 1984, 236-239). There is}
  1327. {an entry in the table for each of the 256 possible 3x3 neighborhood}
  1328. {configurations. An entry of '1' means delete pixel on first pass, '2' means}
  1329. {delete pixel on second pass, and '3' means delete on either pass. There is a}
  1330. {routine in 'user.p' that will draw all 256 neighborhoods.}
  1331.     const
  1332.         s999 = '01234567890123456789012345678901';
  1333.         s000 = '00030033003130330000000030203033';
  1334.         s032 = '00000000300000003000000030003022';
  1335.         s064 = '00000000000000000000000000000000';
  1336.         s096 = '30000000200020003000000030003020';
  1337.         s128 = '03330013000000010000000000000001';
  1338.         s160 = '31000000000000002000000000000000';
  1339.         s192 = '33130013000000010000000000000000';
  1340.         s224 = '3301000100000000330100002200200';
  1341.     var
  1342.         table: FateTable;
  1343.         s: str255;
  1344.         i, pass: integer;
  1345. begin
  1346.     s := concat(s000, s032, s064, s096, s128, s160, s192, s224);
  1347.     for i := 0 to 254 do
  1348.         table[i] := ord(s[i + 1]) - ord('0');
  1349.     table[255] := 0;
  1350.     pass := 0;
  1351.     repeat
  1352.         PixelsRemoved := 0;
  1353.         filter(skeletonize, pass, table);
  1354.         pass := pass + 1;
  1355.         if not CommandPeriod then
  1356.             filter(skeletonize, pass, table);
  1357.         pass := pass + 1;
  1358.     until (PixelsRemoved = 0) or CommandPeriod;
  1359. end;
  1360.  
  1361.  
  1362. procedure DoErosion;
  1363.     var
  1364.         i: integer;
  1365.         t: FateTable;
  1366. begin
  1367.     for i := 0 to BinaryIterations - 1 do begin
  1368.             filter(Erosion, i, t);
  1369.             if CommandPeriod then
  1370.                 leave;
  1371.         end;
  1372. end;
  1373.  
  1374.  
  1375. procedure DoDilation;
  1376.     var
  1377.         i: integer;
  1378.         t: FateTable;
  1379. begin
  1380.     for i := 0 to BinaryIterations - 1 do begin
  1381.             filter(Dilation, i, t);
  1382.             if CommandPeriod then
  1383.                 leave;
  1384.         end;
  1385. end;
  1386.  
  1387.  
  1388. procedure DoOpening;
  1389.     var
  1390.         i: integer;
  1391.         t: FateTable;
  1392. begin
  1393.     for i := 0 to BinaryIterations - 1 do begin
  1394.             filter(Erosion, i, t);
  1395.             if CommandPeriod then
  1396.                 exit(DoOpening);
  1397.         end;
  1398.     for i := 0 to BinaryIterations - 1 do begin
  1399.             filter(Dilation, i + BinaryIterations, t);
  1400.             if CommandPeriod then
  1401.                 exit(DoOpening);
  1402.         end;
  1403. end;
  1404.  
  1405. procedure DoClosing;
  1406.     var
  1407.         i: integer;
  1408.         t: FateTable;
  1409. begin
  1410.     for i := 0 to BinaryIterations - 1 do begin
  1411.             filter(Dilation, i, t);
  1412.             if CommandPeriod then
  1413.                 exit(DoClosing);
  1414.         end;
  1415.     for i := 0 to BinaryIterations - 1 do begin
  1416.             filter(Erosion, i + BinaryIterations, t);
  1417.             if CommandPeriod then
  1418.                 exit(DoClosing);
  1419.         end;
  1420. end;
  1421.  
  1422. procedure SetBinaryCount;
  1423.     var
  1424.         TempCount: integer;
  1425.         Canceled: boolean;
  1426. begin
  1427.     TempCount := GetInt('Neighborhood Pixel Count(1-8):', BinaryCount, Canceled);
  1428.     if Canceled then
  1429.         exit(SetBinaryCount);
  1430.     if (TempCount >= 1) and (TempCount <= 8) then begin
  1431.             BinaryCount := TempCount;
  1432.             BinaryThreshold := BinaryCount * 255
  1433.         end
  1434.     else
  1435.         beep;
  1436. end;
  1437.  
  1438. procedure SetIterations;
  1439.     var
  1440.         TempIterations: integer;
  1441.         Canceled: boolean;
  1442. begin
  1443.     TempIterations := GetInt('Number of Iterations:', BinaryIterations, Canceled);
  1444.     if Canceled then
  1445.         exit(SetIterations);
  1446.     if (TempIterations >= 1) and (TempIterations < 100) then
  1447.         BinaryIterations := TempIterations
  1448.     else
  1449.         beep;
  1450. end;
  1451.  
  1452. procedure ChangeValues (v1, v2, v3: integer);
  1453.   {Changes all pixels in the current selection with a value in the range v1 to v2 to a value of v3.}
  1454.     var
  1455.         id, i, value: integer;
  1456.         table: LookupTable;
  1457. begin
  1458.     if macro then
  1459.         id := ok
  1460.     else begin
  1461.             ParamText(long2str(v1), long2str(v3), '', '');
  1462.             id := alert(700, nil);
  1463.         end;
  1464.     if id = ok then begin
  1465.             for i := 0 to 255 do begin
  1466.                     value := i;
  1467.                     if (value >= v1) and (value <= v2) then
  1468.                         value := v3;
  1469.                     table[i] := value;
  1470.                 end;
  1471.             ApplyTable(table);
  1472.         end;
  1473. end;
  1474.  
  1475. procedure DoPropagate (MenuItem: integer);
  1476.       {Copies the current Look-Up Table, spatial calibration, or density calibration to all open windows.}
  1477.     var
  1478.         TempInfo: InfoPtr;
  1479.         i: integer;
  1480.  
  1481.     procedure CopyLUTInfo;
  1482.     begin
  1483.         with info^ do begin
  1484.                 TempInfo^.RedLUT := RedLUT;
  1485.                 TempInfo^.GreenLUT := GreenLUT;
  1486.                 TempInfo^.BlueLUT := BlueLUT;
  1487.                 TempInfo^.ColorStart := ColorStart;
  1488.                 TempInfo^.ColorEnd := ColorEnd;
  1489.                 TempInfo^.nColors := nColors;
  1490.                 TempInfo^.LutMode := LUTMode;
  1491.                 TempInfo^.cTable := cTable;
  1492.             end;
  1493.     end;
  1494.  
  1495.     procedure CopySpatialCalibration;
  1496.         var
  1497.             SaveInfo: InfoPtr;
  1498.     begin
  1499.         with info^ do begin
  1500.                 TempInfo^.xSpatialScale := xSpatialScale;
  1501.                 TempInfo^.ySpatialScale := ySpatialScale;
  1502.                 TempInfo^.PixelAspectRatio := PixelAspectRatio;
  1503.                 TempInfo^.RawspatialScale := RawspatialScale;
  1504.                 TempInfo^.ScaleMagnification := ScaleMagnification;
  1505.                 TempInfo^.Units := Units;
  1506.                 TempInfo^.UnitsID := UnitsID;
  1507.                 TempInfo^.FullUnits := FullUnits;
  1508.                 TempInfo^.changes := true;
  1509.                 TempInfo^.SpatiallyCalibrated := SpatiallyCalibrated;
  1510.             end;
  1511.         SaveInfo := Info;
  1512.         Info := TempInfo;
  1513.         UpdateTitleBar;
  1514.         Info := SaveInfo;
  1515.     end;
  1516.  
  1517.     procedure CopyDensityCalibration;
  1518.         var
  1519.             SaveInfo: InfoPtr;
  1520.     begin
  1521.         with info^ do begin
  1522.                 TempInfo^.DensityCalibrated := DensityCalibrated;
  1523.                 TempInfo^.ZeroClip := ZeroClip;
  1524.                 TempInfo^.fit := fit;
  1525.                 TempInfo^.nCoefficients := nCoefficients;
  1526.                 TempInfo^.Coefficient := Coefficient;
  1527.                 TempInfo^.UnitOfMeasure := UnitOfMeasure;
  1528.                 TempInfo^.changes := true;
  1529.             end;
  1530.         SaveInfo := Info;
  1531.         Info := TempInfo;
  1532.         UpdateTitleBar;
  1533.         Info := SaveInfo;
  1534.     end;
  1535.  
  1536. begin
  1537.     for i := 1 to nPics do begin
  1538.             TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
  1539.             case MenuItem of
  1540.                 1: 
  1541.                     CopyLUTInfo;
  1542.                 2: 
  1543.                     CopySpatialCalibration;
  1544.                 3: 
  1545.                     CopyDensityCalibration;
  1546.             end; {case}
  1547.         end;
  1548.     WhatToUndo := NothingToUndo;
  1549. end;
  1550.  
  1551. procedure DoArithmetic (MenuItem: integer; constant: extended);
  1552.     var
  1553.         table: LookupTable;
  1554.         i: integer;
  1555.         tmp: LongInt;
  1556.         LogScale: extended;
  1557.         Canceled: boolean;
  1558. begin
  1559.     canceled := false;
  1560.     if not macro then
  1561.         case menuItem of
  1562.             AddItem: 
  1563.                 constant := GetReal('Constant to add:', 25, Canceled);
  1564.             SubtractItem: 
  1565.                 constant := GetReal('Constant to subtract:', 25, Canceled);
  1566.             MultiplyItem:  begin
  1567.                     constant := GetReal('Constant to multiply by:', 1.25, Canceled);
  1568.                     if constant < 0.0 then begin
  1569.                             PutMessage('Constant must be positive.');
  1570.                             exit(DoArithmetic);
  1571.                         end;
  1572.                 end;
  1573.             DivideItem:  begin
  1574.                     constant := GetReal('Constant to divide by:', 1.25, Canceled);
  1575.                     if constant <= 0.0 then begin
  1576.                             PutMessage('Constant must be nonzero and positive.');
  1577.                             exit(DoArithmetic);
  1578.                         end;
  1579.                 end;
  1580.             LogItem:  begin
  1581.                     constant := 0.0;
  1582.                     LogScale := 255.0 / ln(255.0);
  1583.                 end;
  1584.         end; {case}
  1585.     if Canceled then
  1586.         exit(DoArithmetic);
  1587.     for i := 0 to 255 do begin
  1588.             case MenuItem of
  1589.                 AddItem: 
  1590.                     tmp := round(i + constant);
  1591.                 SubtractItem: 
  1592.                     tmp := round(i - constant);
  1593.                 MultiplyItem: 
  1594.                     tmp := round(i * constant);
  1595.                 DivideItem: 
  1596.                     tmp := round(i / constant);
  1597.                 LogItem: 
  1598.                     if i = 0 then
  1599.                         tmp := 0
  1600.                     else
  1601.                         tmp := round(ln(i) * LogScale);
  1602.             end;
  1603.             if tmp < 0 then
  1604.                 tmp := 0;
  1605.             if tmp > 255 then
  1606.                 tmp := 255;
  1607.             table[i] := tmp;
  1608.         end;
  1609.     ApplyTable(table);
  1610. end;
  1611.  
  1612.  
  1613. procedure SortPalette (item: integer);
  1614.     type
  1615.         MyHSVColor = record
  1616.                 lHue, lSaturation, lValue: LongInt;
  1617.             end;
  1618.         HSVRec = record
  1619.                 index: integer;
  1620.                 hsv: MyHSVColor;
  1621.             end;
  1622.         HSVArrayType = array[0..255] of HSVRec;
  1623.     var
  1624.         TempTable: MyCSpecArray;
  1625.         i: integer;
  1626.         HSVArray: HSVArrayType;
  1627.         h, s, v: LongInt;
  1628.         fHue, fSaturation, fValue: fixed;
  1629.         TempHSV: HSVColor;
  1630.         table: LookupTable;
  1631.  
  1632.     procedure SortByHue;
  1633.      {Selection sort routine from "Algorithms" by Robert Sedgewick.}
  1634.         var
  1635.             i, j, min: integer;
  1636.             t: HSVRec;
  1637.     begin
  1638.         for i := 2 to 254 do begin
  1639.                 min := i;
  1640.                 for j := i + 1 to 245 do
  1641.                     if HSVArray[j].hsv.lHue < HSVArray[min].hsv.lHue then
  1642.                         min := j;
  1643.                 t := HSVArray[min];
  1644.                 HSVArray[min] := HSVArray[i];
  1645.                 HSVArray[i] := t;
  1646.             end;
  1647.     end;
  1648.  
  1649.     procedure SortBySaturation;
  1650.         var
  1651.             i, j, min: integer;
  1652.             t: HSVRec;
  1653.     begin
  1654.         for i := 2 to 254 do begin
  1655.                 min := i;
  1656.                 for j := i + 1 to 245 do
  1657.                     if HSVArray[j].hsv.lSaturation < HSVArray[min].hsv.lSaturation then
  1658.                         min := j;
  1659.                 t := HSVArray[min];
  1660.                 HSVArray[min] := HSVArray[i];
  1661.                 HSVArray[i] := t;
  1662.             end;
  1663.     end;
  1664.  
  1665.     procedure SortByValue;
  1666.         var
  1667.             i, j, min: integer;
  1668.             t: HSVRec;
  1669.     begin
  1670.         for i := 2 to 254 do begin
  1671.                 min := i;
  1672.                 for j := i + 1 to 245 do
  1673.                     if HSVArray[j].hsv.lValue < HSVArray[min].hsv.lValue then
  1674.                         min := j;
  1675.                 t := HSVArray[min];
  1676.                 HSVArray[min] := HSVArray[i];
  1677.                 HSVArray[i] := t;
  1678.             end;
  1679.     end;
  1680.  
  1681. begin
  1682.     ShowWatch;
  1683.     DisableDensitySlice;
  1684.     with info^ do begin
  1685.             for i := 1 to 254 do begin
  1686.                     HSVArray[i].index := i;
  1687.                     rgb2hsv(cTable[i].rgb, TempHSV);
  1688.                     with TempHSV do begin
  1689.                             fHue := SmallFract2Fix(hue);
  1690.                             fSaturation := SmallFract2Fix(saturation);
  1691.                             fValue := SmallFract2Fix(value);
  1692.                         end;
  1693.                     with HSVArray[i].hsv do begin
  1694.                             lHue := LongInt(band(fHue, $ffff));
  1695.                             lSaturation := LongInt(band(fSaturation, $ffff));
  1696.                             lValue := LongInt(band(fValue, $ffff));
  1697.                         end;
  1698.                 end;
  1699.             case item of
  1700.                 byHueItem: 
  1701.                     SortByHue;
  1702.                 bySaturationItem: 
  1703.                     SortBySaturation;
  1704.                 byBrightnessItem: 
  1705.                     SortByValue;
  1706.             end;
  1707.             for i := 1 to 254 do begin
  1708.                     with HSVArray[i].hsv do begin
  1709.                             TempHSV.hue := Fix2SmallFract(fixed(lHue));
  1710.                             TempHSV.saturation := Fix2SmallFract(fixed(lSaturation));
  1711.                             TempHSV.value := Fix2SmallFract(fixed(lValue));
  1712.                         end;
  1713.                     hsv2rgb(TempHSV, cTable[i].rgb);
  1714.                 end;
  1715.             LoadLUT(cTable);
  1716.             if info <> NoInfo then begin
  1717.                     table[0] := 0;
  1718.                     table[255] := 255;
  1719.                     for i := 1 to 254 do
  1720.                         table[HSVArray[i].index] := i;
  1721.                     ApplyTable(table);
  1722.                 end;
  1723.             WhatToUndo := NothingToUndo;
  1724.             SetupPseudocolor;
  1725.         end; {with}
  1726. end;
  1727.  
  1728.  
  1729. end.