UNIT jcr6Filter; INTERFACE USES MemTypes, Quickdraw, OSUtils, Packages, FilterInterface, Resources, Memory, Errors; PROCEDURE EntryPoint (selector : INTEGER; stuff : FilterRecordPtr; VAR data : Handle; VAR result : INTEGER); IMPLEMENTATION CONST kMaxCWidth = 4096; {support 4K wide color images} kMaxWidth = 3*kMaxCWidth; {support 12K width B/W images} kFSize = 2; {# lines to examine at once} TYPE TParamRec = RECORD {parameters that need to be set up for this filter, if any} selection : integer; {1..9 contour lines} WhiteBG : Boolean; GreyLines : Boolean; END; TParamPtr = ^TParamRec; TParamH = ^TParamPtr; unsignedbyte = 0..255; C3LineType = Packed Array [0..kMaxWidth-1] of unsignedbyte; {this is for a line that is rgbrgbrgbrgb packed bytes} C3LinePtr = ^C3LineType; {There are NO GLOBAL VARIABLES unless you want to create an A5 world. All of our globals are part of our Handle - storage record, or the data storage handle} PROCEDURE Dispatch (selector : INTEGER; stuff : FilterRecordPtr; VAR data : Handle; VAR result : INTEGER); FORWARD; PROCEDURE EntryPoint (selector : INTEGER; stuff : FilterRecordPtr; VAR data : Handle; VAR result : INTEGER); BEGIN Dispatch(selector, stuff, data, result); END; {EntryPoint} {assorted INLINE calls} FUNCTION DoTestAbort(codeAddress : ProcPtr) : Boolean; {true = user wants to stop} INLINE $205F,$4E90; PROCEDURE DoUpdateProgress (done, total : Longint; codeAddress : ProcPtr); INLINE $205F,$4E90; {get ProcPtr off stack, jump to address with normal params on stack} {utility routines} PROCEDURE CenterDialog(atDLOG : handle); CONST menuHeight = 20; VAR r : Rect; atRect : ^Rect; ScreenWidth : integer; ScreenHeight : integer; ScreenPixMap: PixMapHandle; PROCEDURE Getscreen; CONST ROM85Loc = $28E; VAR WordPtr: ^integer; ColorQDExists: boolean; MainDevice: GDHandle; BEGIN WordPtr := Pointer(ROM85Loc); ColorQDExists := BAND(WordPtr^, $C000) = 0; MainDevice := GetMainDevice; ScreenPixMap := MainDevice^^.gdPMap; {ScreenRowBytes := BitAnd(ScreenPixMap^^.rowBytes, $1fff);} {ScreenBase := ScreenPixMap^^.baseAddr;} END; {GetScreen} BEGIN IF atDLOG=NIL THEN EXIT(CenterDialog); GetScreen; {set up ScreenPixMap} WITH ScreenPixMap^^.bounds DO BEGIN ScreenWidth:=right; ScreenHeight:=bottom; END;{with} atRect:=pointer(atDLOG^); r:=atRect^; WITH r DO OffsetRect(r,-left,-top); WITH r DO OffsetRect(r,(ScreenWidth-right) div 2, (ScreenHeight-bottom-menuHeight) div 3 + menuheight); atRect^:=r; END;{centerdialog} PROCEDURE DoAbout; CONST aboutDITL = 16000; VAR mylog : dialogptr; item : integer; dt : handle; BEGIN dt:=GetResource('DLOG',aboutDITL); IF dt=NIL THEN BEGIN Sysbeep(1); EXIT(DoAbout); END; {Never Never Never mess with a NIL handle} HNoPurge(dt); CenterDialog(dt); mylog:=GetNewDialog(aboutDITL, NIL, WindowPtr(-1)); ModalDialog(NIL,item); {if the user clicks on anything, we're done} DisposDialog(mylog); HPurge(handle(dt)); {this could be dangerous} END;{DoAbout} PROCEDURE OutlineButton(mylog : dialogptr; item : integer); VAR itembox : rect; itemhandle : handle; itemtype : integer; tport : grafptr; BEGIN getport(tport); setport(grafptr(mylog)); GetDItem(mylog,item,itemType,itemHandle,itembox); PenNormal; PenSize(3,3); InsetRect(itembox,-4,-4); FrameRoundRect(itembox,16,16); PenSize(1,1); setport(tport); END;{OutlineButton} FUNCTION GetDString(TheDialog:DialogPtr; item:integer):str255; VAR ItemType:integer; ItemBox:rect; ItemHdl:handle; astr:str255; BEGIN GetDItem (TheDialog,item,ItemType,ItemHdl,ItemBox); GetIText(ItemHdl,astr); GetDString:=astr; END;{getdstring} PROCEDURE SetDString(TheDialog:DialogPtr; item:integer; astr:str255); VAR ItemType:integer; ItemBox:rect; ItemHdl:handle; BEGIN GetDItem (TheDialog,item,ItemType,ItemHdl,ItemBox); SetIText(ItemHdl,astr) END;{setdstring} FUNCTION GetDNum(TheDialog:DialogPtr; item:integer):LongInt; VAR ItemType:integer; ItemBox:rect; ItemHdl:handle; astr:str255; n:LongInt; BEGIN GetDItem (TheDialog,item,ItemType,ItemHdl,ItemBox); GetIText(ItemHdl,astr); StringToNum(astr,n); GetDNum:=n; END;{getdnum} PROCEDURE SetDNum(TheDialog:DialogPtr; item:integer; n:longint); VAR ItemType:integer; ItemBox:rect; ItemHdl:handle; astr:str255; BEGIN GetDItem (TheDialog,item,ItemType,ItemHdl,ItemBox); NumToString(n,astr); SetIText(ItemHdl,astr) END;{setdnum} PROCEDURE SetDialogItem(TheDialog:DialogPtr; item,value:integer); VAR ItemType:integer; ItemBox:rect; ItemHdl:handle; BEGIN GetDItem (TheDialog,item,ItemType,ItemHdl,ItemBox); SetCtlValue(ControlHandle(ItemHdl),value) END; {setdialogitem} PROCEDURE Dispatch; {prototype declared above} VAR gresult : oserr; {to be passed out} isTopEdge : boolean; isBottomEdge : boolean; FUNCTION CommandPeriod : Boolean; BEGIN CommandPeriod:=DoTestAbort(stuff^.abortProc); END;{CommandPeriod} PROCEDURE ShowProgress (done,total : longint); BEGIN DoUpdateProgress(done,total,stuff^.progressProc); END;{ShowProgress} PROCEDURE DoParameters; {ask user for plug-in's parameters. The image size is undefined at this point. This may not be called each time that the plug-in is executed depending on how selected from menu} CONST ParamDITL = 16001; VAR r : rect; tport : grafptr; dt : Handle; item : integer; mylog : DialogPtr; myselection : integer; {1..9 contour lines} MyWhiteBG, MyGreyLines : Boolean; BEGIN InitCursor; IF stuff^.parameters=NIL THEN WITH stuff^ DO BEGIN {allocate space for parameters to be stored} parameters:=NewHandleClear(sizeof(TParamRec)); IF parameters=NIL THEN BEGIN gResult:=memFullErr; {-108} EXIT(DoParameters); END; TParamH(parameters)^^.selection:=0; {set up default parameter values} TParamH(parameters)^^.WhiteBG:=false; TParamH(parameters)^^.GreyLines:=true; END; {if,with - parameters=NIL} WITH stuff^ DO BEGIN TParamH(parameters)^^.selection:=1; {set up default parameter values} TParamH(parameters)^^.WhiteBG:=true; TParamH(parameters)^^.GreyLines:=true; dt:=GetResource('DLOG',ParamDITL); if dt=NIL then BEGIN sysbeep(1); EXIT(DoParameters); END; {never mess with a NIL handle} HNoPurge(dt); CenterDialog(dt); mylog:=GetNewDialog(ParamDITL,NIL,WindowPtr(-1)); mySelection:=TparamH(parameters)^^.selection; myWhiteBG :=TparamH(parameters)^^.WhiteBG; myGreyLines:=TparamH(parameters)^^.GreyLines; SetDialogItem(MyLog,5,ord(MyWhiteBG)); SetDialogItem(MyLog,4,ord(MyGreyLines)); SetDNum(MyLog,3,myselection); SelIText(MyLog,3,0,32767); item:=0; REPEAT ModalDialog(NIL,item); if (item=5) THEN BEGIN MyWhiteBG:= NOT MyWhiteBG; SetDialogItem(MyLog,5,ord(MyWhiteBG)); END; if (item=4) THEN BEGIN MyGreyLines:= NOT MyGreyLines; SetDialogItem(MyLog,4,ord(MyGreyLines)); END; if (item=3) then myselection:=GetDNum(mylog,3); UNTIL ((item=1) and (myselection>=1) and (myselection<=10)) or (item=2); IF item=2 THEN gResult:=1 {guy hit cancel} ELSE BEGIN TParamH(parameters)^^.selection:=myselection; TParamH(parameters)^^.WhiteBG :=myWhiteBG; TParamH(parameters)^^.GreyLines:=myGreyLines; END;{else good result} DisposDialog(mylog); hPurge(handle(dt)); END;{with} END; {DoParameters} PROCEDURE DoPrepare; {if we require large amounts of memory, set the stuff^.bufferSpace field to indicate how much (bytes)} BEGIN WITH stuff^ DO BEGIN {if we were setting up a large matrix, look-up tables, etc} stuff^.bufferSpace:=0; END; {with} END;{DoPrepare} PROCEDURE DoStart; {request pointer to first part of image to be filtered} BEGIN WITH stuff^ DO BEGIN {select top line, assume bottom is non-inclusive} inrect:=filterrect; inrect.bottom:=inRect.top+1; {this is one line at a time} isTopEdge:=true; isBottomEdge:=false; {select all planes} inLoPlane:=0; inHiPlane:=planes-1; outrect:=inRect; outrect.bottom:=outRect.top+1; {only write out 1 line} {output planes} outLoPlane:=0; outHiPlane:=planes-1; END; {with} END;{DoStart} PROCEDURE DoContinue; {processes the area requested and requests the next area} VAR x : integer; S1Ptr,S2Ptr,T1Ptr : C3LinePtr; width : integer; Level : Array[1..10] of integer; NumLevels : integer; glines : integer; i,c,Blank : integer; prec,prev,value,compare : integer; flag,MyWhiteBG,MyGreyLines : Boolean; BEGIN WITH stuff^ DO BEGIN NumLevels:=TParamH(parameters)^^.selection; MyWhiteBG:=TParamH(parameters)^^.WhiteBG; MyGreyLines:=TParamH(parameters)^^.GreyLines; {check for cancel} IF CommandPeriod THEN BEGIN gResult:=1; {cancel} EXIT(DoContinue); END; FOR i:=1 to NumLevels DO LeveL[i]:=(i*255) div (1+NumLevels); S1Ptr:=pointer(inData); T1Ptr:=pointer(outData); glines:=inRect.bottom-inRect.top; CASE gLines OF 1: BEGIN S2Ptr:=S1Ptr; END; 2: BEGIN S2Ptr:=pointer(ord4(S1Ptr)+inRowBytes); END; END; {case} {process this portion} width:=inRect.right-inRect.left; if planes<>3 then {B/W, or something else} BEGIN width:=width*planes; {if planes<>1 and <>3} {S1,S2->T1} {x:=0;} {_,b,c _,e,f _,h,i} IF MyWhiteBG THEN Blank:=255 Else Blank:=0; prec:=S1Ptr^[0];{left} For c:=0 to width-1 DO BEGIN prev :=S1Ptr^[c]; value:=S2Ptr^[c]; flag:=false; For i:=1 to NumLevels DO BEGIN compare:=Level[i]; if (((value<=compare) and (prec>compare)) OR ((value>compare) and (prec<=compare))) OR (((value<=compare) and (prev>compare)) OR ((value>compare) and (prev<=compare))) THEN BEGIN IF MyGreyLines THEN T1Ptr^[c]:=compare ELSE T1Ptr^[c]:=255-Blank; flag:=true; END; END;{for i} if NOT flag THEN T1Ptr^[c]:=Blank; prec:=value; END; {for c} END ELSE {color, 3 planes} BEGIN {what to do with a color image?} END;{else color} {update progress bar} ShowProgress(outRect.top,filterRect.bottom); {request next output portion} WITH outRect DO BEGIN top:=top+1; bottom:=bottom+1; END; {ask for new source rect} if outRect.bottom>filterrect.bottom then BEGIN SetRect(inRect,0,0,0,0); {flag that we're done} outrect:=inrect; END else BEGIN inrect.top:=outrect.top-1; inrect.bottom:=outrect.bottom; END; END;{with stuff^} END;{DoContinue} PROCEDURE DoFinish; BEGIN {this is used to throw away any temporary storage that may have been allocated in DoStart. called even if user cancels} END;{DoFinish} VAR firstTime : Boolean; {quasi-global} BEGIN {Dispatch - MAIN} {Main dispatching routine, Initialize and set up "global" variables, perform operation specified by selector} {I'm allowed to set up a data area, as a handle that I allocate the first time through and gets disposed by Photoshop} firstTime:=data=NIL; if FirstTime then data:=NewHandle(sizeof(longint)); gResult:=NoErr; {initialize the global error that may accumulate in one on the sub-procedures} CASE selector OF filterSelectorAbout : DoAbout; filterSelectorParameters : DoParameters; {We're calling DoParameters in DoPrepare} filterSelectorPrepare : DoPrepare; filterSelectorStart : DoStart; filterSelectorContinue : DoContinue; filterSelectorFinish : DoFinish; END; {case} result:=gResult; END; {Dispatch} END. {unit}