; ; File: ColorMgr.a ; ; Copyright: © 1981-1990, 1992-1993 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 11/29/93 SAM Yet another getCTable mod. Move the handle to the clut copy ; into A0 prior to the seed range check. ; 11/7/93 SAM Changed GetCTable to get a new seed if the id > 1023 (kMinSeed). ; 9/29/93 SAM From mc900ftjesus ; 9/27/93 SAM Rewrote the guts of GetCTable. The result of calling GetCtable ; is a Handle to a ram based copy of the clut (whether its in ROM ; or not). It no longer requires that you have 2x sixeof(clut) ; heap space. Made GetCTable not set ctSeed if the ct ID is ² 8. ; 9/13/93 SAM Changed all instances of _Translate24to32 to _rTranslate24to32 ; so they can conditionalized out of the build. ; 3/3/93 PN Fix the rGetResource in MakeITable by adjusting the CurMap to ; point to SysMap before calling rGetResource. Roll in patch ; StartRGetResourceFromSystemMapFormitqResources ; 1/21/93 KW (LW3 fau) Rolled in the MySaveEntries patch: In SaveEntries, if ; the bpp is ³ 16bpp it will exit with an error, but the code was ; not returning the stack to it's correct state. Renamed all the ; labels in SaveEntries that started with a SE to SaveEnt so they ; don't clash with the ones in SetEntries, that also start with ; SE. ; 12/19/92 HI Modified GetCTable to copy the 'clut' resources instead of ; just detaching the resource handle because sometimes GetCTable ; modifies the 'clut' but it can't sice the SuperMario 'clut' ; resource overrides the System file's resource. Hence a RAM copy ; of the ROM's 'clut' resource must be made. This fixes the bug ; of machine freezing when switching to 16 colors. (Hoon Im) ; 6/11/92 stb stb add comments from QDciPatchROM.a to MakeITable, ; AdjZone, Collapse, NewHandleTempSysApp, Index2Color, ; InvertColor, DelSearch, DelComp; this file is synched with ; QDciPatchROM.a ; <16> 1/18/91 dvb Don't restore color environment if it hasn't been changed. ; Modify SetEntries to mark flag in QDSpare. ; <15> 11/6/90 KON GetCTable should return NIL if call fails. This is at an offset ; of 6, not 8. [SMC] ; <14> 10/29/90 KON If GetCTable fails, it should return NIL. There were cases where ; it returned without setting the return value at all. ; <13> 9/17/90 BG Removed <10>. 040s are now behaving more reliably. ; <12> 8/21/90 KON No need to _HLock and _HUnlock search element handle in ; Color2Index. ; <11> 7/24/90 gbm change a duplicate symbol or two ; <10> 6/25/90 BG Added EclipseNOPs to deal with flakey 040s. ; <9> 5/29/90 KON Make DelSearch and DelComp jive with ci patch. ; <8> 5/29/90 KON Call Translate24to32 on addresses passed to DelSearch and ; DelComp so they match the addresses in the gDevice. ; <7> 3/26/90 KON Use NewHandle,sys rather than change zone when getting memory ; for ITable. Fix comp proc bug in _InvertColor. ; <6> 3/2/90 KON If app heap is the same as sys heap, only check for memory once. ; <5> 3/1/90 KON Change MakeITable so it first looks in Tmp memory, then in sys ; memory, and finally theZone for memory. ; <4> 1/31/90 BAL Fixed replication of 5 bit components in Index2Color to use the ; same algorithm as Search16toIndexed (stretch.a) in order to make ; SeedCFill/CalcCMask happy. ; <3> 1/30/90 DAF Removed CLUTBusy asynchronous flags from SetEntries. They ; didn't work well and broke Studio/8. ; <2> 1/16/90 BAL Changed MakeITab to restore theZone during error returns. ; <¥2.3> 7/14/89 BAL For Aurora: Final CQD ; <2.2> 6/30/89 BAL GetCTable will not modify 2 bit CLUT if hilite color is a gray. ; ¶ ; <2.1> 6/15/89 BAL Changed source to use ReserveBit instead of Reserve; Fixed bug ; in makeGray tab. ; <2.0> 6/12/89 BAL Added ; to 1.9 ; <1.9> 6/12/89 BAL Stopped Color2Index,Index2Color from clearing QDErr. Rolled in ; fix to MyGetCTable. ; <1.8> 6/10/89 CEL Moved Private.a QuickDraw Equates into proper QuickDraw private ; file (colorequ.a), got rid of QuickDraw nFiles dependencies and ; fixed up necessary filesÉ ; <¥1.7> 5/29/89 BAL Blasting in 32-Bit QuickDraw version 1.0 Final ; <1.6> 5/15/89 DAF Corrected GetCTable (actually myGetCTable) to always return a ; non-purgeable cTabHandle ; 5/14/89 DAF Fixed GetCTable to return non-purgeable handle if from system ; resource. ; <1.5> 4/20/89 GGD Fixed assembly error (missing semicolon) so that the ROMs can ; build again. ; <1.4> 4/19/89 DAF Rolled in system disk patch to SetEntries (additional error ; checking). ; 4/19/89 DAF Rolled in SaveEntries patch from system file. ; <¥1.3> 4/12/89 BAL Blasting in 32-Bit QuickDraw 1.0B1 ; 4/5/89 DAF Added additional error checking to RestoreEntries. ; 4/24/87 DAF Fixed MakeITable not to SysError on memory failures (from ; romfix78) ; 2/20/87 DAF MakeITable gets queue sizes from a resource now. Exported ; ITabMatch and vector to it from Color2Index. ReserveEntry ; changes ctSeed when deactivating reserve bit. ; 2/15/87 DAF Fixed displacement into hidden color table in ITabMatch to work ; correctly in 5-bit res mode. ; 2/9/87 DAF Reactivated usage of .value fields for protect, reserve and ; ownerID. Made ReserveEntry change ctSeed. ; 2/3/87 DAF Separated ProtErr test for protect and reserve in SetEntries ; (err only if .value field doesn't match clientID. Adjusted ; ReserveEntry to set clientID when reserve=TRUE. Added range and ; protection error checking to SetCommon. Adjusted seeding code in ; MakeITable for common loop for single loop exit, and to seed ; original colors in a different order. ; 2/2/87 DAF Fixed MakeITable to test if result table can be allocated early, ; so it doesn't need to SysError, and cleaned up zone setting. ; SetEntries now returns a ProtErr when attempting to change ; reserved entries. RealColor fixed to compare only iTable ; resolution bits of each channel. Updated error codes throughout. ; 1/26/87 DAF Made _Control calls immediate. Added range-checking for res and ; default color- and inverse tables to MakeITable ; 1/22/87 DAF Fixed trashed registers in MakeSubTable. Changed searchProc ; chain to be handles instead of pointers. Added ; DeleteSearch,DeleteComp routines. ; 1/15/87 DAF Fixed 5-Bit ITable building by expanding TblSize to a long ; 12/30/86 DAF Made some minor bug fixes to SetCommon and MakeITable ; 12/6/86 DAF Made fixes from code review. Changed interfaces of ; Color2Index,Index2Color, and SetEntry (now SetEntries) ; 11/9/86 DAF Refined Save/RestoreEntries (added reqLSize field), improved ; error handling added QDErr routine. ; 11/4/86 DAF Added Save/RestoreEntries ; 10/26/86 DAF Updated SetEntry to call video driver ; 10/20/86 DAF Fixed TFB CLUT address temporarily ; 10/19/86 DAF Added ITSeed to inverse tables. Made ITabMatch rebuild invalid ; iTable ; 10/13/86 EHB Delete packCLUT and unpackCLUT. No packed CLUTs! ; 10/7/86 DAF Removed temp patches in SetEntry ; 10/5/86 DAF Added a number of unimplemented routines ; 8/12/86 EHB Added CTSeed field to color tables built by unpackClut ; 8/12/86 EHB INIT VALUE FIELD TO ENTRY NUMBER ; 7/24/86 DAF New today ; ; To Do: ; ;EASE$$$ READ ONLY COPY of file ÒColorMgr.aÓ ;¥2.3 BAL 07/14/1989 For Aurora: Final CQD ; 2.2 BAL 06/30/1989 GetCTable will not modify 2 bit CLUT if hilite color is a gray. ¶ ;Changed MakeITable to use MFTemp memory if allocation of its work buffer in the app heap fails.¶ ;If MakeITable returns an error, ITabMatch will use the stale inverse table instead of aborting. ; 2.1 BAL 06/15/1989 Changed source to use ReserveBit instead of Reserve; Fixed bug in makeGray tab. ; 2.0 BAL 06/12/1989 Added ; to 1.9 ; 1.9 BAL 06/12/1989 Stopped Color2Index,Index2Color from clearing QDErr. Rolled in fix to MyGetCTable. ; 1.8 CEL 06/10/1989 Moved Private.a QuickDraw Equates into proper QuickDraw ; private file (colorequ.a), got rid of QuickDraw nFiles dependencies ; and fixed up necessary filesÉ ;¥1.7 BAL 05/29/1989 Blasting in 32-Bit QuickDraw version 1.0 Final ; 1.6 DAF 05/15/1989 Corrected GetCTable (actually myGetCTable) to always ; return a non-purgeable cTabHandle ; 1.5 GGD 04/20/1989 Fixed assembly error (missing semicolon) so that the ROMs can ; build again. ; 1.4 DAF 04/19/1989 Rolled in system disk patch to SetEntries (additional ; error checking). ;¥1.3 BAL 04/12/1989 Blasting in 32-Bit QuickDraw 1.0B1 ; ; File ColorMgr.a ; ; Copyright Apple Computer, Inc. 1981-1986 ; All Rights Reserved ; ; ;------------------------------------------ ; ; Color Manager ; ; This code provides services for matching RGB color specifications to ; color look-up table indices, implements the device-independent layer ; of functions such as setting color table entries, and includes ; some useful utilities for dealing with pixelmaps. ; ; Modification History: ; ---------------------------- ; ; 24-Jul-86 DAF New today ; 12AUG86 EHB INIT VALUE FIELD TO ENTRY NUMBER ; 12Aug86 EHB Added CTSeed field to color tables built by unpackClut ; 05Oct86 DAF Added a number of unimplemented routines ; 13oct86 EHB Delete packCLUT and unpackCLUT. No packed CLUTs! ; DAF Added ITSeed to inverse tables. Made ITabMatch rebuild ; invalid iTable ; DAF Fixed TFB CLUT address temporarily ; DAF Updated SetEntry to call video driver ; DAF Added Save/RestoreEntries ; DAF Removed temp patches in SetEntry ; DAF Refined Save/RestoreEntries (added reqLSize field), improved error handling ; added QDErr routine. ; DAF Made fixes from code review. Changed interfaces of Color2Index,Index2Color, ; and SetEntry (now SetEntries) ; DAF Made some minor bug fixes to SetCommon and MakeITable ; DAF Fixed 5-Bit ITable building by expanding TblSize to a long ; DAF Fixed trashed registers in MakeSubTable. Changed searchProc chain to be ; handles instead of pointers. Added DeleteSearch,DeleteComp routines. ; DAF Made _Control calls immediate. Added range-checking for res and default ; color- and inverse tables to MakeITable ; DAF Fixed MakeITable to test if result table can be allocated early, so it ; doesn't need to SysError, and cleaned up zone setting. SetEntries now ; returns a ProtErr when attempting to change reserved entries. RealColor ; fixed to compare only iTable resolution bits of each channel. Updated ; error codes throughout. ; DAF Separated ProtErr test for protect and reserve in SetEntries (err only if ; .value field doesn't match clientID. Adjusted ReserveEntry to set clientID ; when reserve=TRUE. Added range and protection error checking to SetCommon. ; Adjusted seeding code in MakeITable for common loop for single loop ; exit, and to seed original colors in a different order. ; DAF Reactivated usage of .value fields for protect, reserve and ownerID. Made ; ReserveEntry change ctSeed. ; DAF Fixed displacement into hidden color table in ITabMatch to work correctly ; in 5-bit res mode. ; DAF MakeITable gets queue sizes from a resource now. Exported ITabMatch ; and vector to it from Color2Index. ReserveEntry changes ctSeed when ; deactivating reserve bit. ;_______________________________________________________________________ ; ; Post Mac II Release ;_______________________________________________________________________ ; ; <24Apr87> DAF Fixed MakeITable not to SysError on memory failures (from romfix78) ;_______________________________________________________________________ ; ; The Colorful World of 32-bit QuickDraw ; ; <05Apr89> DAF Added additional error checking to RestoreEntries. ; <19Apr89 DAF Rolled in SaveEntries patch from system file. ; <14May89> DAF Fixed GetCTable to return non-purgeable handle if from ; system resource. ; <30Jan98 DAF Removed CLUTBusy change-blocking flag in SetEntries ;_______________________________________________________________________ MACHINE MC68020 ;--------------------------- ; TEMPORARY EXPORTS OF UNIMPLEMENTED ROUTINES. UPDATEPIXMAP PROC EXPORT RTS ; ; QUEUE and DEQUEUE : macro definitions for circular queue maintainance, as ; used by MakeITable. They expect : A4 = queue insertion pointer ; A5 = end of queue memory ; A1 = queue removal pointer ; QStart(A6) = start of queue memory ; D0 is trashed MACRO QUEUE ®1,®2 ; CMP.L A4,A5 ; is it time to wrap? BNE.S @88 ; nope, so continue MOVE.L QStart(A6),A4 ; wrap back @88 MOVE.L ®1,D0 ; copy it to D0 ADD.L ®2,D0 ; and add the other reg MOVE.W D0,(A4)+ ; put it in the queue ENDM MACRO DEQUEUE ® ; CMP.L A1,A5 ; is it time to wrap? BNE.S @97 ; nope, so continue MOVE.L QStart(A6),A1 ; wrap back @97 CMP.L A4,A1 ; is it done? BNE.S @98 ; not done, so get a displacement MOVEQ #0,® ; return the completion signal BRA.S @99 ; @98 MOVE.W (A1)+,® ; get the next displacement @99 ENDM MakeITable PROC EXPORT EXPORT MakeGrayITab ;---------------------------------------------------------------- ; ; PROCEDURE MakeITable (CTABH: CTabHandle; ITabH: ITabHandle; res:integer); ; ; This procedure builds an inverse lookup table with res bits per channel ; based on the specified color table. Algorithm by MJ Potel, based on Voronoi theory. ; Algorithm modified by Art Cabral, adding a linked list at the end of the table ; which is used to find hidden colors in Color2Index. ; ; Still to be done : Consolidate Queue and table into 1 memory block ; Convert to bitmap/iLUT form ; Eliminate BTST for TST.B ; ; 1. Use supplied cTabHandle and iTabHandle. This makes it context independent. ; 2. Compress directly into iTable. A little faster. ; 3. Shouldn't need to to error checking in QUEUE and DEQUEUE ; contains the fix from QDciPatchROM.a to first search temp memory, then system stb ; zone, then finally theZone. stb UNDEF EQU $8000 ; hi bit on BOUND EQU $7FFF ; non-neg, large unused index QInTempBit EQU $00 ITableInTempBit EQU $01 PARAMSIZE EQU 10 ; total bytes of params CTabH EQU PARAMSIZE+8-4 ; Color table handle ITabH EQU CTabH-4 ; ITable handle RES EQU ITabH-2 ; entry size QStart EQU -4 ; pointer to the queue SvZone EQU QStart-4 ; save the current zone TblH EQU SvZone-4 ; copy of our temp ITable TblSize EQU TblH-4 ; size of our temp ITable InfoOffset EQU TblSize-4 ; offset to ITInfoTable from ITabH isTmpHandle EQU InfoOffset-2 ; boolean->true if TblH is a TmpMem handle QHandle EQU isTmpHandle-4 ; Handle for queue <28Feb90 KON> VARSIZE EQU QHandle LINK A6,#VARSIZE ; set up a stack frame MOVEM.L A2-A5/D3-D7,-(SP) ; save 'em all ; move.l theZone,SvZone(A6) ; save the current heap zone for later restore <16Jan90> BAL CLR.W QDErr ; clear QDError flag DAF MOVE res(A6),D7 ; get the channel size EHB CMP.L #-1,theGDevice ; if -1, then it's uninitialized DAF BEQ.S @ResCheck ; OK, so continue DAF @GDevOK MOVE.L ([theGDevice]),A0 ; get a pointer to the current device DAF ; test the parameters. If ITabH or CTabH are NIL, substitute handles from theGDevice DAF TST.L ITabH(A6) ; is ITabH NIL? DAF BNE.S @DoCTabH ; no, so continue DAF MOVE.L GDITable(A0),ITabH(A6) ; stick theGDevice's ITabH in stack frame DAF @DoCTabH TST.L CTabH(A6) ; is CTabH NIL? DAF BNE.S @DoITabRes ; no, so continue DAF MOVE.L ([GDPMap,A0]),A1 ; get pixMap's handle DAF MOVE.L PMTable(A1),CTabH(A6) ; get the device colorTable's pointer DAF @DoITabRes ; get the ITable resolution TST D7 ; test the channel size EHB BNE.S @ResCheck ; if valid, continue DAF ; the res parameter was 0, so get theGDevice's preferred resolution DAF MOVE.W GDResPref(A0),D7 ; get the preferred ITabRes DAF BNE.S @ResSubst ; if non-zero, then continue DAF MOVEQ #4,D7 ; if resPref=0, then force to 4 DAF @ResSubst MOVE.W D7,res(A6) ; and put it in the stack frame DAF @ResCheck CMP.W #2,D7 ; don't accept res²2 DAF BLE.S @BadRes ; DAF CMP.W #6,D7 ; or ³ 6 DAF BLT.S @ResOK ; DAF @BadRes MOVE.W #cResErr,QDErr ; mark the error and quit DAF BRA MITDone ; DAF @ResOK ; DAF ; Calculate the size of the compressed table and verify the handle MOVE D7,D1 ; get the channel size DAF MOVEQ.L #1,D0 ; prime the register LSL.L D1,D0 ; calculate 2^^res LSL.L D1,D0 ; square it LSL.L D1,D0 ; cube it ADD.L #ITTable,D0 ; make room for everybody MOVE.L D0,InfoOffset(A6) ; save offset to end of table ADD.L #ITExtraSize,D0 ; make room for InfoTable MOVE.L ITabH(A6),A0 ; get the ITable handle TST.L (A0) ; is the table purged? DAF BEQ.S @1 ; yes, so Realloc it DAF _SetHandleSize ; set the size DAF BRA.S @2 ; and continue DAF @1 _ReallocHandle ; DAF @2 BEQ.S @SizeOK ; => size ok, so continue DAF MOVE.W #CNoMemErr,QDErr ; report this error DAF BRA MITDone ; and quit DAF @SizeOK ;--------------------------------------------------------------------- ; ; Cruise through the clut and check if all non-reserved entries are grays. ; If they are then build a gray Itab and set flag in Info header to indicate grayness. ; move.l CTabH(a6),a0 ; move.l (a0),a0 ; get CTab ptr move.w ctSize(a0),d0 ; get clut entries-1 lea ctTable(a0),a1 ; point at first clut entry @nextEntry move.l (a1)+,d1 ; pick up value,red move.l (a1)+,d2 ; pick up green,blue cmp.w d1,d2 ; red==blue? bne.s @notGray ; swap d2 ; get green cmp.w d1,d2 ; red==green? bne.s @notGray ; @skippy dbra d0,@nextEntry ; continue checking _MakeGrayITab ; go make special Gray ITab bra MITDone ; all done @notGray btst #ReserveBit+24,d1 ; is this entry reserved? bne.s @skippy ; don't check reserved entries ;--------------------------------------------------------------------- AdjZone ; this is where the patch used to move.w #8080 to force the call to DisposeTempBuffer, stb ; but now, having had the opportunity to fix the code elsewhere, we donÕt have to be stb ; so tricky, and we can go back to the normal way of doing things. stb move.w #0,isTmpHandle(a6) ;assume handles are not in temp memory <28Feb90 KON> ; Here is the fix for rGetResource from ResourceMgrPatches.a move.w CurMap,-(sp) ; Save the current resource map. move.w SysMap,CurMap ; <27> Start searching from the System file instead of the top ; allocate space on the heap for the queue, reading queue size from resource SUBQ #4,SP ; make room for the function return DAF MOVE.L #'mitq',-(SP) ; push the queue size resource type DAF CLR.W -(SP) ; want mitq=0 DAF _rGetResource ; system first, then ROM DAF MOVE.L (SP)+,A0 ; get the handle DAF move.w (sp)+,CurMap ; Restore the current resource map MOVE.L (A0),A0 ; get the pointer in A0 DAF MOVE.L -12(A0,D7*4),D0 ; get the queue size (adjust for no 1 or 2 bit resolutions) DAF MOVE.L D0,D3 ; hold it for a second bsr NewHandleTempSysApp ;get memory <28Feb90 KON> BNE MakeIErrLate ; if there was an error report it ; ; Got a handle. Check which heap it's in and lock it. ; tst.w d1 ;d1=0 -> memory in temp heap <28Feb90 KON> bne.s @notTemp1 ; <28Feb90 KON> bset #QInTempBit, isTmpHandle(a6) ;Main ITable is in temp memory <28Feb90 KON> @notTemp1 ; ; want a ptr, so lock and deref handle ; move.l a0,QHandle(a6) ;save QHandle <28Feb90 KON> _HLock ;preserves a0 <28Feb90 KON> move.l (a0),a0 ;deref it <28Feb90 KON> MOVE.L A0,QStart(A6) ; save the starting addr in stack frame ADD.L D3,A0 ; compute the addr of the queue end MOVE.L A0,A5 ; keep the queue end here ; allocate BIG space on the heap for the ITable (to be shrunken and moved to sysheap later) MOVEQ #1,D6 ; prime the register LSL.L D7,D6 ; calculate 2^^res MOVE.L D6,D5 ; save here for boundary setting ADDQ #2,D6 ; increase by 2 for the boundary MOVE.L D6,D0 ; get it in a working register MULU.W D6,D0 ; square it MULU.W D6,D0 ; cube it ADD.L D0,D0 ; table size -> words MOVE.L D0,TblSize(A6) ; save size of table bsr NewHandleTempSysApp ;search everywhere for some memory <28Feb90 KON> bne MakeIErr ;failed! <28Feb90 KON> ; ; Got memory, check which heap it's in ; tst.w d1 ;d1=0 -> memory in temp heap <28Feb90 KON> bne.s @notTemp2 ; <28Feb90 KON> bset #ITableInTempBit, isTmpHandle(a6) ;Main ITable is in temp memory <28Feb90 KON> @notTemp2 MOVE.L A0,TblH(A6) ; save temporary table MOVE.L (A0),A0 ; dereference the handle MOVE.L A0,A3 ; set up the table base ptr ; and mark the boundaries of the cube. (thanks, Ernie) Boundary MOVE.L #((Bound)++(Bound<<16)),D1 ; get BOUND.LONG DAF MOVE.L #((UNDEF)++(UNDEF<<16)),D0 ; GET UNDEF.LONG DAF ; DO TOP EXCEPT ONE ROW MOVE D6,D4 ;GET WIDTH OF TOP MULU D6,D4 ;GET AREA OF TOP SUB D6,D4 ;BACK UP ONE ROW SUBQ #1,D4 ;MAKE 0 BASED LSR #1,D4 ;CONVERT TO LONGS (moved conversion!!) CLRTOP MOVE.L D1,(A0)+ ;SET TOP TO BOUND DBRA D4,CLRTOP ;=>UNTIL DONE ; FOR EACH MIDDLE SLAB, SET 2 ROWS TO BOUND ; THEN ONE LONG OF BOUND FOLLOWED BY WIDTH/2 LONGS OF UNDEF SUB.L #2,A0 ;SO RIGHT/LEFT EDGES IN SAME LONG MOVE D5,D4 ;GET NUMBER OF MIDDLE SLABS TO DO SUBQ #1,D4 ;MAKE 0 BASED CLRSLAB MOVE.L D6,D3 ;DO 2 BOUND ROWS SUBQ #1,D3 ; and adjust counter TWOROWS MOVE.L D1,(A0)+ ;SET TO BOUND DBRA D3,TWOROWS ;=>UNTIL DONE MOVE.L D5,D2 ; get the number of rows in a layer SUBQ #1,D2 ; convert to counter ClrLayer MOVE.L D1,(A0)+ ;PREVIOUS RIGHT, NEXT LEFT = BOUND MOVE D5,D3 ;INNER ROW = UNDEF LSR #1,D3 ; convert to long SUBQ #1,D3 ; make this zero based, too CLRROW MOVE.L D0,(A0)+ ;SET TO UNDEF DBRA D3,CLRROW ;=>UNTIL DONE DBRA D2,ClrLayer ;do each row of a layer DBRA D4,CLRSLAB ;=>DO NEXT SLAB ; DO BOTTOM PLUS ONE ROW PLUS ONE WORD MOVE D1,(A0)+ ;DO THE EXTRA WORD MOVE D6,D4 ;GET WIDTH OF BOTTOM MULU D6,D4 ;GET AREA OF BOTTOM ADD.L D6,D4 ;ADD ONE ROW SUBQ #1,D4 ;adjust for counter LSR #1,D4 ;CONVERT TO LONGS (moved conversion!!) CLRBOT MOVE.L D1,(A0)+ ;SET BOTTOM TO BOUND DBRA D4,CLRBOT ;=>UNTIL DONE ; Time to queue the seed colors. First, make some lookup tables for the ; rgb components. This is necessary, since the "big" cube is (2^^n)+2 ; in size, and the 3 indices can't be shifted and ORed together. These ; lookup tables are word sized, and looked up with a byte index. Each ; entry is repeated 2^^(8-res) times, which saves truncating the values. ; Since these tables are fairly small, and the queue is large, we build ; them at the end of the queue's memory space. Seed ; first, calculate the inner and outer loop counts MOVE.L D6,D4 ; calculate 2^^res (outer loop count) SUBQ #3,D4 ; and turn it into a counter MOVE.L #8,D3 ; compute 2^^(8-res) (inner loop count) SUB res(A6),D3 ; get 8-res MOVEQ #1,D5 ; prime for a power of two calc LSL D3,D5 ; and get power of 2 in D5 SUBQ #1,D5 ; and turn it into a counter ; next, calculate increments and starting values MOVE.L D6,D7 ; copy cube edge size to D7 MULU D6,D7 ; square it (size^^2 is increment for red) MOVE.L D7,D0 ; init red value to include ADD.L D6,D0 ; offset to (1,1,1) of the big cube ADDQ #1,D0 ; MOVEQ #0,D1 ; init green value MOVEQ #0,D2 ; init blue value ; now figure out where the table starts. It's easy since the table is a ; constant size = 256 * 4 words = 1024 words = $800 bytes MOVE.L A5,A0 ; copy the queue end addr SUB.L #$800,A0 ; table starts here MOVE.L A0,A2 ; remember it here ; OK, let's get to it! To make lookup easy, we multiplex the three tables ; together so it looks like RGBxRGBxRGBx. The "x" value lets us get to ; these entries using SCALE*8 @1 MOVE.L D5,D3 ; set up inner loop counter @2 MOVE.W D0,(A0)+ ; write red MOVE.W D1,(A0)+ ; write green MOVE.W D2,(A0)+ ; write blue ADDQ #2,A0 ; skip a word DBRA D3,@2 ; for some number of times ADD.W D7,D0 ; add size^^2 to red value ADD.W D6,D1 ; add size to green value ADD.W #1,D2 ; add one to blue value DBRA D4,@1 ; ; Queue the seed colors, marking their positional value in the color ; table in the big cube (don't use the colorSpec.index field!!) ; MOVE.L QStart(A6),A4 ; initialize the queue insertion point to the beginning ADDQ #2,A4 ; bump the start by a little MOVE.L ([ITabH,A6]),A1 ; get a pointer to the colorTable ADD.L InfoOffset(A6),A1 ; bump the pointer past the big table to the info stuff CLR.W ITabHidden(A1) ; no hidden entries yet CLR.W ITabReserved(A1) ; no extra fields yet CLR.W ITabFlags(A1) ; not a gray ITab ; ; Black and white's index values have special significance to QuickDraw in old grafPorts. ; By default, they are the first and last colors in the colortable. We queue them ; first so that they will never be overridden by identical colors in the interior of the ; colorTable. If B&W are not in their normal positions, old grafports will look funny ; anyway, so we don't need to check. D5 has the colorTable index to queue. ; DAF - Queuing order changed (search for *&*& to see end of change) MOVE.L ([CTabH,A6]),A0 ; get a pointer to the colorTable MOVEQ #0,D5 ; the first element is normally white (clr top half of D5, too) BSR.S QUtil ; queue it MOVE.W CTSize(A0),D5 ; the last element is normally black BSR.S QUtil ; queue it ; ; order doesn't matter for the remaining colors. clut=8 has best results if we seed ; moving forward so let's do it that way. ; MOVE.L D5,D4 ; copy total # of color (and cleared hi word) SUBQ #2,D4 ; this is the number of colors remaining BLT.S QVille ; if zero or negative, all done MOVEQ #1,D5 ; start at color #1 (zero based) @TheRestOfUs ; a temporary label for the rest of us BSR.S QUtil ; queue it ADDQ #1,D5 ; advance to next DBRA D4,@TheRestOfUs ; and loop til done BRA.S QVille ; and continue ; ; here's an inline queuing utility. This allows us to queue in any order, but particularily ; we can try to queue black and white first, to make sure that they are in the inverse ; table even though, by default they are at opposite ends of the colorTable. It's inline ; so it can be a short branch around it. ; QUtil BTST #ReserveBit,CTTable+value(A0,D5*8) ; is this color reserved? DAF BNE.S @5 ; if so, don't queue DAF MOVEQ #0,D0 ; clear the index register MOVEQ #0,D1 ; init the lookup register MOVE.B CTTable+rgb+red(A0,D5*8),D0 ; get the HI BYTE of red value MOVE.W 0(A2,D0*8),D1 ; get the adjusted red value MOVE.B CTTable+rgb+green(A0,D5*8),D0 ; get the HI BYTE of green value ADD.W 2(A2,D0*8),D1 ; add the adjusted green value MOVE.B CTTable+rgb+blue(A0,D5*8),D0 ; get the HI BYTE of blue value ADD.W 4(A2,D0*8),D1 ; add the adjusted blue value MOVE.W (A3,D1.L*2),D2 ; fetch the current value BMI.B @4 ; if Undef (<0) then just use it MOVE.B ITabInfo(A1,D2),ITabInfo(A1,D5) ; current.link := old.link MOVE.B D5,ITabInfo(A1,D2) ; old.link := current ADDQ.W #1,ITabHidden(A1) ; bump the number of hidden entries BRA.S @5 ; branch to end of loop DAF @4 MOVE.B D5,ITabInfo(A1,D5) ; make it self-referential MOVE.W D5,(A3,D1.L*2) ; put the color in the cube QUEUE D1,#0 ; queue the displacement @5 ; DAF RTS ; and return ;*&*& end of DAF ; ; Now it's time to propagate the seed values throughout the ; cube. The procedure is fairly straightforward- dequeue an element ; from the queue, then mark and queue its six neighbors until the ; cube is full. D6 still has the cube edge size (green delta), ; and D7 still has the size of a plane (red delta). ; QVille MOVE.L QStart(A6),A1 ; set the removal ptr to the 1st valid entry ADDQ.L #2,A1 ; bump the end up to match the first entry MOVEQ #1,D5 ; set up the blue delta BRA @7 ; jump to end of loop (save 1 inst) @1 LEA (A3,D1.L*2),A0 ; get the address of the queued element MOVE.W (A0),D4 ; get the color value in this position (note that hi bit is set {that's good!}) BTST #7,(A0,D5.L*2) ; check the next blue BEQ.S @2 ; if nonzero, then skip it MOVE.W D4,(A0,D5.L*2) ; mark it as taken QUEUE D1,D5 ; and queue the index @2 NEG.L D5 BTST #7,(A0,D5.L*2) ; check the alternate blue BEQ.S @3 ; if nonzero, then skip it MOVE.W D4,(A0,D5.L*2) ; mark it as taken QUEUE D1,D5 ; and queue the index @3 BTST #7,(A0,D6.L*2) ; check the next green BEQ.S @4 ; if nonzero, then skip it MOVE.W D4,(A0,D6.L*2) ; mark it as taken QUEUE D1,D6 ; and queue the index @4 NEG.L D6 BTST #7,(A0,D6.L*2) ; check the alternate green BEQ.S @5 ; if nonzero, then skip it MOVE.W D4,(A0,D6.L*2) ; mark it as taken QUEUE D1,D6 ; and queue the index @5 BTST #7,(A0,D7.L*2) ; check the next red BEQ.S @6 ; if nonzero, then skip it MOVE.W D4,(A0,D7.L*2) ; mark it as taken QUEUE D1,D7 ; and queue the index @6 NEG.L D7 BTST #7,(A0,D7.L*2) ; check the alternate red BEQ.S @7 ; if neg, then skip it MOVE.W D4,(A0,D7.L*2) ; mark it as taken QUEUE D1,D7 ; and queue the index MOVEQ #0,D1 ; clear the hi half of the offset register @7 DEQUEUE D1 ; pull a queue element BNE @1 ; if the new one is zero, then done (0,0,0 is a boundary and never queued) ; ; the iLUT is now built. Compress it down to a byte table and resize the handle ; Collapse MOVE.L ITabH(A6),A0 ; get the ITable handle MOVE.L (A0),A0 ; get pointer to ITable MOVE.L ([CTabH,A6]),A2 ; get a pointer to the colortable DAF MOVE.L CTSeed(A2),(A0)+ ; get the color table's seed value (itabSeed) MOVE RES(A6),(A0)+ ; write out resolution (itabRes) MOVE.L TblSize(A6),D0 ; get the cube size LSR.L #1,D0 ; divide by 2 to get entry count in words SUBQ #1,D0 ; and convert to counter for DBRA MOVE.L TblH(A6),A2 ; get the cube handle MOVE.L (A2),A2 ; get the cube pointer @1 MOVE.W (A2)+,D1 ; get an element of the expanded table CMP.W #BOUND,D1 ; is it a boundary cell? BEQ.S @2 ; if so, then skip it MOVE.B D1,(A0)+ ; if not, then write byte in final table @2 DBRA D0,@1 ; loop through entire cube MOVE.L TblH(A6),A0 ; get the temporary handle ; from QDciPatchROM.a MakeITable patch stb btst #ITableInTempBit, isTmpHandle(a6) ;is handle in temp memory <28Feb90 KON> beq.s @notTmp ; no, use normal dispose <2.2> BAL _DisposeTempBuffer ; bag it <2.2> BAL bra.s @allGone ; continue <2.2> BAL @notTmp _DisposHandle ; release it @allGone ; ; dispose of the memory for the Queue ; DisposeQ ; <24Apr87> DAF MOVE.L QHandle(A6),A0 ; release the queue <28Feb90 KON> btst #QInTempBit, isTmpHandle(a6) ;is Queue in temp memory <28Feb90 KON> beq.s @QnotTmp ; no, use normal dispose <28Feb90 KON> _DisposeTempBuffer ; bag it <28Feb90 KON> bra.s @QallGone ; continue <28Feb90 KON> @QnotTmp _DisposHandle ; release it <28Feb90 KON> @QallGone ; <28Feb90 KON> MITDone ; DAF ; MOVE.L SvZone(A6),theZone ; always restore the zone (needed for error exits) MOVEM.L (SP)+,A2-A5/D3-D7 ; restore registers UNLK A6 ; bag the stack frame RTD #ParamSize ; flush parameters and return home DAF MakeIErr ; <24Apr87> DAF MOVE.W #CTempMemErr,QDErr ; report a table allocation error bra.s DisposeQ MakeIErrLate MOVE.W #CTempMemErr,QDErr ; report a table allocation error BRA.S MITDone ;----------------- Subroutine to hunt for memory ----------------------------- ; from QDciPatchROM.a stb NewHandleTempSysApp ; ; Called with requested size in d0. Routine first tries to get memory first in ; multifinder temp memory, then sys heap, then app heap. If memory found in ; temp memory, d1 returns 0, otherwise d1 is 1. ; ; Returns with: d0 error, 0=noErr ; a0 handle if no error ; ; Try temp memory if multifinder exists ; move.l d0, -(sp) ;save requested size moveq #0, d1 ;assume temp memory _NewTempHandle ;look for it in temp memory cmp.l (sp),d0 ;is it the size we requested? beq.s @GotMem ;yes, it's big enough _DisposeTempBuffer ;A0 already has handle of wrong size ; ; Try sys memory ; @noMF moveq #1, d1 ;not temp memory move.l (sp),d0 _NewHandle ,sys beq.s @GotMem ; ; Try app memory if zone different from system zone ; move.l theZone,d0 cmp.l sysZone, d0 ;have we already tried this zone? beq.s @Memoryerr ;don't try it again move.l (sp),d0 _NewHandle bne.s @MemoryErr ;couldn't find the memory anywhere @GotMem moveq #0,d0 ;signal noErr @MemoryErr addq #4,sp ;remove requested memory size tst.w d0 ;set condition codes rts MakeGrayITab ;----------------------------------------------------------------------------- ; ; MakeGrayITab ; ; At his point we know that all the clut entries are grays. ; ; First allocate a word-sized luminosity table on the stack and fill ; it with the appropriate index for each level by a seed fill method. ; Then compact the luminosity table into the ITInfoTable at the end of the ITab. ; ; Finally fill the ITab out with the appropriate index for each luminosity ; mapped position. ; ;PARAMSIZE EQU 10 ; total bytes of params ;CTabH EQU PARAMSIZE+8-4 ; Color table handle ;ITabH EQU CTabH-4 ; ITable handle ;RES EQU ITabH-2 ; entry size ; ;QStart EQU -4 ; pointer to the queue ;SvZone EQU QStart-4 ; save the current zone ;TblH EQU SvZone-4 ; copy of our temp ITable ;TblSize EQU TblH-4 ; size of our temp ITable ;InfoOffset EQU TblSize-4 ; offset to ITInfoTable from ITabH ;VARSIZE EQU InfoOffset ; Allocate and initialize table on stack moveq #-1,d0 ; initializer for table moveq #63,d1 ; # of double longs-1 in table @1 move.l d0,-(sp) ; init 2 entries move.l d0,-(sp) ; init 2 entries dbra d1,@1 ; Seed table with clut entries clr.w 510(sp) ; seed white to guarantee last entry move.w #255,(sp) ; seed black to guarantee 1st entry move.l CTabH(a6),a0 ; move.l (a0),a0 ; point at CLUT move.w ctSize(a0),d0 ; get # entries -1 moveq #0,d2 ; clear out high end of word @nxtEntry move.w d0,d1 ; assume entry = index btst #ReserveBit,ctTable+value(a0,d0*8) bne.s @skip ; ignore reserved entries ; tst.w TransIndex(a0) ; look for flag in transIndex ASSUME DEVICE CLUT ; bmi.s @UseIndex ; => flag set, use index ; move.w ctTable+value(a0,d0*8),d1 ; get value of current entry FUNG'S A HOSER @UseIndex move.b ctTable+rgb+red(a0,d0*8),d2 ; get luminance=red move.w d1,(sp,d2*2) ; put pixel value in luminace table @skip dbra d0,@nxtEntry ; => repeat until done ; Repeatedly smear right and then left one position until no changes in table move.l a7,a1 ; add.w #512,a1 ; set up limit pointer @smear moveq #0,d1 ; clear changed flag lea 2(a7),a0 ; point at second entry @right cmp.l a0,a1 ; reached end of table yet? ble.s @end tst.w (a0)+ ; is this entry occupied? bpl.s @right ; yes, keep going move.w -4(a0),-2(a0) ; no, smear right addq #2,a0 ; don't smear more than one position st d1 ; set changed flag bra.s @right @end subq #2,a0 ; stay inside last entry @left cmp.l a0,a7 ; reached beginning of table yet? bge.s @doneSmear tst.w -(a0) ; is this entry occupied? bpl.s @left ; yes, keep going move.w 2(a0),(a0) ; no, smear left subq #2,a0 ; don't smear more than one position st d1 ; set changed flag bra.s @left @doneSmear tst d1 ; were there any changes? bne.s @smear ; yes, smear some more ; Now compress the table into the ITInfoTable and discard the stack version. move.l ITabH(a6),a1 ; move.l (a1),a1 ; move.l ([CTabH,a6]),a2 ; get a pointer to the colortable move.l CTSeed(a2),iTabSeed(a1) ; get the color table's seed value move RES(a6),iTabRes(a1) ; write out resolution add.l InfoOffset(a6),a1 ; point at ITInfoTable bset #15,iTabFlags(a1) ; set grayITab flag move.w #255,d0 ; get table size lea ITabLuma(a1),a3 ; point at luma table @copy move.b 1(sp,d0*2),d1 ; get index move.b d1,ITabInfo(a1,d0) ; copy into ITInfoTable move.b ctTable+rgb+red(a2,d1*8),(a3,d0) ; get actual luminance=red for dither error table dbra d0,@copy add.w #512,sp ; discard temp table ; Now fill out the ITab with luminance mapped index values at each location. MOVE.L ([ITabH,a6]),A0 ; get the iTable's master pointer MOVEQ #1,D0 ; prime the register again MOVE ITabRes(A0),D2 ; get the inverse table resolution (and Bit-field width) LSL.L D2,D0 ; calculate 2^res subq #1,d0 ; make it zero based for dbra LEA ITTable(A0),A0 ; point us at the ITab data lea ITabInfo(a1),a1 ; bump past header move #2*256,d4 ; start with max blue contribution lsr.w d2,d4 ; divide by # of steps move #9*256,d5 ; start with max green contribution lsr.w d2,d5 ; divide by # of steps sub #2*256,d5 ; adjust for reverse blue bump move #5*256,d6 ; start with max red contribution lsr.w d2,d6 ; divide by # of steps sub #9*256,d6 ; adjust for reverse green bump ;------------------------------------------------------- ; a0 = ITTable d0 = 2^iTabRes ; a1 = ITabInfo d1 = red looper ; a2 = cur luma d2 = green looper ; a3 = d3 = blue looper ; a4 = d4 = blue bump ; a5 = d5 = green bump - max blue ; a6 = locals d6 = red bump - max green ; a7 = d7 = scratch ;------------------------------------------------------- sub.l a2,a2 ; init composite luminance move.w d0,d1 ; init red position @red move.w d0,d2 ; init green position @green move.w d0,d3 ; init blue position @blue move.w a2,d7 ; make a copy lsr.w #4,d7 ; normalize luminance move.b (a1,d7),(a0)+ ; store index in table add d4,a2 ; bump in blue dbra d3,@blue add d5,a2 ; bump back to zero blue coordinate and increment green dbra d2,@green add d6,a2 ; bump back to zero green coordinate dbra d1,@red ; bra MITDone ; all done rts ;----------------------------------------------------------------------------- ; ; function ITabMatch ( myColor : RGBColor; var MatchPos : longint ) : boolean; ; ; This procedure takes an RGBColor and finds the best match ; to the .rgb field by lookup in the inverse table. The resultant ; index value is extended to a long and returned as the result. ; As the default routine, it does not look at the CDID field and ; always returns a result. It uses the linked list at the end of the ; ILUT to find and return "hidden" colors, based on a simple best fit ; calculation. ; ITabMatch PROC EXPORT PARAMSIZE EQU 10 ; total bytes of params Result EQU PARAMSIZE+8-2 MyColor EQU Result-4 MatchPos EQU MyColor-4 Best EQU -2 ; keeps track of our index during hidden searches BestVal EQU Best-2 ; keeps track of the best value while searching EndLink EQU BestVal-2 ; the self-referential link that signals stop ITabInfoPtr EQU EndLink-4 ; pointer to hidden colors, etc VARSIZE EQU ITabInfoPtr LINK A6,#VARSIZE ; set up a stack frame MOVE.L A2,-(SP) ; save a register DAF CLR.B Result(A6) ; assume boolean false MOVE.L ([theGDevice]),A2 ; get a pointer to the current device MOVE.L ([GDPMap,A2]),A1 ; get pixMap's handle MOVE.L PMTable(A1),A0 ; get the device colorTable's handle MOVE.L ([A0],CTSeed),D1 ; get a pointer to the device colortable MOVE.L ([GDITable,A2]),D0 ; get the Itable's master pointer CMP.L (ITabSeed,ZA0,D0.L),D1 ; has the colortable changed? BEQ.S @1 ; if equal, then the iTable is OK ; if table is not up to date, build a new one MOVE.L A0,-(SP) ; push theGDevice's color table handle MOVE.L GDITable(A2),-(SP) ; push theGDevice's current iTabHandle MOVE.W GDResPref(A2),-(SP) ; push the preferred iTableResolution _MakeITable ; make a new table ; TST.W QDErr ; was this successful? <2.2> BAL ; BNE MtchEnd ; nope, so quit <2.2> BAL MOVE.L ([theGDevice]),A2 ; redereference in case it moved MOVE.L ([GDITable,A2]),D0 ; get the iTable's master pointer @1 MOVE.L D0,A0 ; and get a pointer to the ITab MOVEQ #1,D0 ; prime the register again MOVE ITabRes(A0),D2 ; get the inverse table resolution (and Bit-field width) LSL.L D2,D0 ; calculate 2^^res LSL.L D2,D0 ; square it LSL.L D2,D0 ; cube it LEA ITTable(A0,D0.L),A1 ; point us at the ITabInfo move.l a1,ITabInfoPtr(a6) ; save for later tst.w iTabFlags(a1) ; is this a grayITab? bpl.s @notGray ; Now we know that all matchable clut entries are grays so simply compute luminance ; of input RBG and return index from 256 entry luminance table at end of the grayITab. move.l myColor(a6),a0 ; get a pointer to the RGBColor lea red(a0),a0 ; point at red cmp moveq #0,d0 ; clear out high end moveq #0,d1 ; clear out high end moveq #0,d2 ; clear out high end move.w (a0)+,d0 ; get the red component move.w (a0)+,d1 ; get the green component move.w (a0),d2 ; get the blue component ; Compute Luminance = ((((((r+g)/2)+b)/2+r)/2)+g)/2 move.l d1,a0 ; copy green add.l d0,d1 lsr.l #1,d1 add.l d2,d1 lsr.l #1,d1 add.l d0,d1 lsr.l #1,d1 add.l a0,d1 lsr.l #1,d1 lsr.l #8,d1 ; make into byte value move.b ITabInfo(A1,D1),D1 ; pick up index for this luminance bra FoundIt ; => D1 has the answer @notGray MOVE ITabRes(A0),D2 ; get the inverse table resolution (and Bit-field width) MOVE.L myColor(A6),A1 ; get a pointer to the RGBColor BFEXTU red(A1){0:D2},D0 ; get the red component (BFEXTU zero extends) LSL.L D2,D0 ; and shift it over res bits BFEXTU green(A1){0:D2},D1 ; get the green component OR.L D1,D0 ; put them together LSL.L D2,D0 ; shift over again BFEXTU blue(A1){0:D2},D1 ; get the blue component OR.L D1,D0 ; and here's the completed index ; BFEXTU clears high 3 bytes of D1 MOVE.B ITTable(A0,D0),D1 ; get the index value ; Now we have the index, but we may not have the best match it can offer. See if ; it is self referential within the ITabInfo table, in which case it is probably the best move.l ITabInfoPtr(a6),a1 ; point us at the ITabInfo CMP.B ITabInfo(A1,D1),D1 ; is this entry self referential? BEQ.S FoundIt ; => D1 has the answer ; Where we keep some stuff: ; A0 - pointer to target RGBColor ; A1 - pointer to ITabInfo ; A2 - pointer to color table of current device ; D0 - calculates and holds the "distance" ; D1 - best index found ; D2 - used to calculate difference MOVE.L myColor(A6),A0 ; get a pointer to the RGBColor MOVE.L ([theGDevice]),A2 ; get the current device pointer MOVE.L ([GDPMap,A2]),A2 ; get the device pixelmap pointer MOVE.L ([pmTable,A2]),A2 ; point to the color table MOVE.W D1,Best(A6) ; save current index - high byte is 0 MOVE.W D1,EndLink(A6) ; terminate when Current.Link = EndLink MOVE.W #$7FFF,BestVal(A6) ; BestVal := $7FFF ; Okay, we have to look through the hidden values until we find the best match. This is a ; crude and simple search - we estimate the "distance" between our target and a hidden value ; based upon max(abs(ÆRed),max(abs(ÆBlue),abs(ÆGreen))). We select the index which has the minimum ; "distance", stopping early if we find an exact match. Repeatr MOVE.W CTTable+rgb+red(A2,D1.W*8),D0 ; get red value of current index SUB.W Red(A0),D0 ; subtract target red BPL.S @1 ; make it positive NEG D0 ; like so @1 MOVE.W CTTable+rgb+green(A2,D1.W*8),D2 ; get green value of current index SUB.W Green(A0),D2 ; subtract target green BPL.S @2 ; make it positive NEG D2 ; like so @2 CMP.W D2,D0 ; select D0 := max(D2,D0) BGE.S @3 MOVE.W D2,D0 @3 MOVE.W CTTable+rgb+blue(A2,D1.W*8),D2 SUB.W Blue(A0),D2 BPL.S @4 NEG D2 @4 CMP.W D2,D0 BGE.S @5 MOVE.W D2,D0 @5 TST.W D0 ; check result of our calculations BEQ.S FoundIt ; D1 has the best match CMP.W BestVal(A6),D0 BGE.S NotBest ; Set BestVal to the newly calculated match and update BestIndex MOVE.W D0,BestVal(A6) ; copy new best value into holder MOVE.W D1,Best(A6) ; copy current index into result NotBest MOVE.B ITabInfo(A1,D1.W),D1 ; get the next link - high byte still 0 CMP.W EndLink(A6),D1 ; are we done? BNE.S Repeatr MOVE.W Best(A6),D1 ; grab the best match into D1 FoundIt MOVE.L MatchPos(A6),A0 ; get pointer to index result EXT.L D1 ; make sure the upper word is clear MOVE.L D1,(A0) ; write result MOVE.B #1,Result(A6) ; and return boolean true MtchEnd MOVE.L (SP)+,A2 ; restore register (was 2) DAF UNLK A6 ; release stack frame RTD #8 ; and go home ColorThing2Index PROC EXPORT ; EXPORT Color2Index ; ; ; function ColorThing2Index ( myThing : ColorThing ) : longint; ; ; This procedure takes an RGB colorSpec-like thing and returns ; the appropriate pixel value for the current GDevice (despite how much I ; hate the concept of THEGDevice damn it!). But atleast we can share code. ; ; ColorThing format colorSpace equ 0 ;[byte] 0=RGB, 1=palette index, 2=? ctFiller equ colorSpace+1 ;[byte] reserved for clut compatibility colorData equ ctFiller+1 ;actual color stuff, 0->48bit RGB, 1->16bit palette index result EQU 12 ; longword result myThing EQU 8 ; pointer to colorThing retAddr EQU 4 ; return address LongRes EQU -4 ; space for a longint return FrameSize EQU LongRes ; LINK A6,#FrameSize ; clr.l -(sp) ; make room for result move.l myThing(a6),a0 ; get thing ptr move.w (a0)+,-(sp) ; push palette index move.l a0,myThing(a6) ; set to RGB ptr for color2Index moveq #0,d0 ; Entry2Index selector ; dc.w $aaa2 ; ask the brink jsr ([$e00+(4*$2a2)]) ; ask the brink (faster) move.l (sp)+,result(a6) ; get the answer move.w qderr,d0 ; is it real? bne.s c2iShare UNLK A6 ; release stack frame RTD #4 ; return and dealloc ; ; function Color2Index ( myColor : RGBColor ) : longint; ; ; This procedure takes an RGB color specification and finds it's best ; match using the search rule chain. The resultant index value is ; returned right-justified in the longword result. Note that direct ; mapping devices MUST have a searchProc. ; Color2Index ;PROC EXPORT ; interface modified DAF ;result EQU 12 ; longword result DAF MyColor EQU 8 ; pointer to rgbColor DAF ;retAddr EQU 4 ; return address DAF ;LongRes EQU -4 ; space for a longint return DAF ;FrameSize EQU LongRes ; LINK A6,#FrameSize ; DAF c2iShare MOVE.L A2,-(SP) ; save a register DAF ; CLR.W QDErr ; clear the error register <1.9> MOVE.L ([theGDevice]),A2 ; get a pointer to the device port if 0 then MOVE.L GDSearchProc(A2),D0 ; get the head of the search chain @1 BEQ.S DefRule ; if NIL, then apply default rule MOVE.L D0,A2 ; save the search element handle DAF MOVE.L D0,A0 ; and set up for HLock DAF _HLock ; lock the search element (so A2 will be valid after searchProc) DAF SUBQ #2,SP ; make room for boolean result DAF MOVE.L MyColor(A6),-(SP) ; push RGBColor pointer DAF PEA LongRes(A6) ; push pointer to placeholder DAF MOVE.L (A2),A0 ; get pointer to search element DAF MOVE.L SrchProc(A0),A0 ; get the searchProc address DAF JSR (A0) ; execute routine DAF MOVE.L A2,A0 ; unlock the search element now DAF _HUnlock ; DAF TST.B (SP)+ ; test the boolean result DAF BNE.S Found1 ; if result = TRUE then quit DAF MOVE.L (A2),A0 ; get pointer to this search element again DAF MOVE.L NxtSrch(A0),D0 ; get pointer to next DAF BRA.S @1 ; endif ;-----------Optimization <20AUG90 KON> Hopefully it still works-------------------------------- MOVE.L GDSearchProc(A2),a2 ; get the head of the search chain <20AUG90 KON> NxtProc MOVE.L A2,D0 ; To work register <20AUG90 KON> beq.s DefRule ; no search proc, or end of list <20AUG90 KON> MOVE.L (A2),A0 ; get pointer to search element DAF move.l NxtSrch(a0),a2 ; get handle to next search element <20AUG90 KON> ; ; Call search proc ; SUBQ #2,SP ; make room for boolean result DAF MOVE.L MyColor(A6),-(SP) ; push RGBColor pointer DAF PEA LongRes(A6) ; push pointer to placeholder DAF MOVE.L SrchProc(A0),A0 ; get the searchProc address DAF JSR (A0) ; execute routine DAF TST.B (SP)+ ; test the boolean result DAF beq.s NxtProc ; not found, try next search proc <20AUG90 KON> ;-----------END <20AUG90 KON> ----------------------------------------------------------------- Found1 MOVE.L LongRes(A6),D0 ; get the longword index DAF MOVE.L D0,result(A6) ; return result DAF MOVE.L (SP)+,A2 ; restore saved register DAF UNLK A6 ; release stack frame DAF RTD #4 ; return and dealloc DefRule MOVE.L ([theGDevice]),A0 ; get a pointer to the device port DAF CMP.W #DirectType,GDType(A0) ; if it is direct, don't call ITabMatch DAF BEQ.S @DirectPix ; calc direct pixel value DAF CLR.B -(SP) ; make room for function result DAF MOVE.L myColor(A6),-(SP) ; push RGBColor pointer PEA LongRes(A6) ; push pointer to index placeholder DAF _ITabMatch ; call the default routine TST.B (SP)+ ; test the boolean result DAF ;BNE.S GetIt ; if OK result, get index DAF bra.s GetIt ; just get index anyway @DirectPix MOVE.L ([GDPMap,A0]),A1 ; get the device pixelmap pointer move.w cmpSize(a1),d1 ; get cmp size BAL MOVE.L myColor(A6),A0 ; point to the color spec DAF moveq #0,d0 ; clear a register BAL MOVE.B red(A0),D0 ; get the red channel DAF LSL.L d1,D0 ; shift over DAF MOVE.B green(A0),D0 ; get the green channel DAF LSL.L d1,D0 ; shift again DAF MOVE.B blue(A0),D0 ; get the blue channel DAF neg.w d1 addq.w #8,d1 ; shift needed to right justify pixel ble.s @noShift ; needed for cmpSize=10 lsr.l d1,d0 @noShift MOVE.L D0,LongRes(A6) ; just for now! DAF GetIt BRA.S Found1 ; and return DAF ; ; procedure Index2Color ( index : longint; VAR myColor : RGBColor ); ; ; This procedure takes an index value and returns the RGB ; components of this element of theGDPort^^.GDPMap.pmTable. ; ; as seen in QDciPatchROM.a stb Index2Color PROC EXPORT MOVE.L 8(SP),D0 ; get the index DAF MOVE.L 4(SP),A0 ; get pointer to result RGB DAF MOVE.L ([theGDevice]),A1 ; get the current device pointer MOVE.L ([GDPMap,A1]),A1 ; get the device pixelmap pointer cmp #16,pixelType(a1) ; is it a direct device? beq.s I2CDirect ; yes, no CLUT MOVE.L ([pmTable,A1]),A1 ; point to the color table ; CLR.W QDErr ; clear the error register <1.9> MOVE.W ctSize(A1),D1 ; get ctSize DAF CMP.W D1,D0 ; is index in range? DAF BGT.S I2CErr ; nope, so quit DAF MOVE.W CTTable+rgb+red(A1,D0.L*8),red(A0) ; move red DAF MOVE.L CTTable+rgb+green(A1,D0.L*8),green(A0) ; move green & blue DAF I2CDone RTD #8 ; and return I2CErr MOVE.W #cRangeErr,QDErr ; set error code BRA.S I2CDone ; I2CDirect ; Handle 24bit/pixel direct devices moveq #8,d1 ; get handy constant sub.w cmpSize(a1),d1 ; shift needed to left justify cmp beq.s I2C24 ; cmpsize is 8 so go fast ;enter with a 16-bit source pixel in d0 lsl.l #3,d0 ;left align blue in lo byte move.b d0,d1 lsl.l #5,d1 ;get 5 bits of blue move.b d0,d1 lsl.l #5,d1 ;make into 10 bits of blue move.b d0,d1 lsl.l #5,d1 ;make into 15 bits of blue move.b d0,d1 lsr.l #7,d1 ;get a word of blue MOVE.w d1,blue(a0) ;store out in color spec lsr.l #5,d0 ;left align green in lo byte move.b d0,d1 lsl.l #5,d1 ;get 5 bits of green move.b d0,d1 lsl.l #5,d1 ;make into 10 bits of green move.b d0,d1 lsl.l #5,d1 ;make into 15 bits of green move.b d0,d1 lsr.l #7,d1 ;get a word of green MOVE.w d1,green(a0) ;store out in color spec lsr.l #5,d0 ;left align red in lo byte move.b d0,d1 lsl.l #5,d1 ;get 5 bits of red move.b d0,d1 lsl.l #5,d1 ;make into 10 bits of red move.b d0,d1 lsl.l #5,d1 ;make into 15 bits of red move.b d0,d1 lsr.l #7,d1 ;get a word of red MOVE.w d1,red(a0) ;store out in color spec if 0 then lsl.l d1,d0 ; put cmp at high end of byte move.w cmpSize(a1),d1 ; get distance to next cmp move.b d0,d2 ; make a copy lsl.w d1,d2 ; preserve cmpSize bits in high byte move.b d0,d2 ; replicate cmpSize bits lsr.w d1,d2 ; make into a byte move.b d2,blue(a0) ; set up high byte of blue result move.b d2,blue+1(a0) ; set up low byte of blue result move.w d0,d2 ; make a copy of cmpSize bits in high byte lsr.l d1,d0 ; get next cmp in high end of lo byte move.b d0,d2 ; replicate cmpSize bits lsr.w d1,d2 ; make into a byte move.b d2,green(a0) ; high green move.b d2,green+1(a0) ; low green move.w d0,d2 ; make a copy of cmpSize bits in high byte lsr.l d1,d0 ; get next cmp in high end of lo byte move.b d0,d2 ; replicate cmpSize bits lsr.w d1,d2 ; make into a byte move.b d2,red(a0) ; high red move.b d2,red+1(a0) ; low red endif bra.s I2CDone I2C24 move.b d0,blue(a0) ; set up high byte of blue result move.b d0,blue+1(a0) ; set up low byte of blue result lsr.w #8,d0 ; get green byte move.b d0,green(a0) ; high green move.b d0,green+1(a0) ; low green swap d0 ; get red byte move.b d0,red(a0) ; high red move.b d0,red+1(a0) ; low red bra.s I2CDone ; ; procedure InvertColor ( VAR myColor : RGBColor ); ; ; This procedure takes an RGBColor, calculates its inverse using the current ; inverse rules, then updates the provided RGBColor. It only uses the ; compProc interface if the default is not called. ; ; has fix from QDciPatchROM.a where an offset was wrong because of a byte result stb InvertColor PROC EXPORT MOVE.L A2,-(SP) ; save a register DAF MOVE.L ([theGDevice]),A2 ; get a pointer to the device port MOVE.L GDCompProc(A2),D0 ; get the head of the complement chain @1 BEQ.S DefComp ; if NIL, then apply default rule MOVE.L D0,A2 ; save the pointer DAF MOVE.L D0,A0 ; set up to lock this element DAF _HLock ; lock it down so that A2 will be valid after the JSR DAF CLR.B -(SP) ; room for a boolean result MOVE.L 8+2(SP),-(SP) ; push the pointer to the color <26MAR90 KON> MOVE.L (A0),A0 ; get the element pointer DAF MOVE.L CompProc(A0),A0 ; get the complement proc ptr DAF JSR (A0) ; execute routine MOVE.L A2,A0 ; set up for HUnlock DAF _HUnlock ; release the compElement DAF TST.B (SP)+ ; test the result BNE.S FoundComp ; if result = TRUE then quit MOVE.L (A2),A0 ; get the compElement pointer again DAF MOVE.L NxtComp(A0),D0 ; get pointer to next DAF BRA.S @1 ; FoundComp MOVE.L (SP)+,A2 ; restore the register DAF RTD #4 ; and go home ; the default procedure is VERY simple. It just gets the complement of each ; component. It always returns a sucessful result. DefComp MOVE.L 8(SP),A0 ; get pointer to RGBColor NOT.W (A0)+ ; complement red NOT.W (A0)+ ; complement green NOT.W (A0) ; complement bleu, svp. BRA.S FoundComp ; and it always works too. ; ; procedure RGBForeColor (Color : RGBColor); ; ; This routine takes an RGB triple and sets the current Graf- or CGrafPort ; fields so that drawing will take place with the best match of the ; requested color, using the current GDevice's color matching rules. The ; appropriate fields are set depending on portType ; RGBForeColor PROC EXPORT EXPORT RGBBackColor,RGB2OLD MOVEQ #FGCOLOR,D0 ; get offset to index field MOVEQ #RGBFgColor,D1 ; get offset to RGB field SHARE MOVE.L 4(SP),A1 ; point at the RGB color MOVE.L GRAFGLOBALS(A5),A0 ; get the QuickDraw globals pointer MOVE.L THEPORT(A0),A0 ; point at the port PEA 0(A0,D0) ; save pointer to the index field TST PORTBITS+ROWBYTES(A0) ; is it a new port? BMI.S NEWPORT ; => yes, set RGB components JSR RGB2OLD ; else convert RGB to old color BRA.S DONE ; => and return ; Palette manager patch rolled in <6Jun87 EHB> ; Flag to the Palette Manager whether it a foreground or a background call happened NEWPORT MOVE.L GrafVars(A0),D2 ; get the GrafVars handle <6Jun87 EHB> BEQ.S NoPM ; =>no grafVars, continue <6Jun87 EHB> MOVE.L D2,A1 ; else get GrafVars handle <6Jun87 EHB> MOVE.L (A1),A1 ; point to GrafVars <6Jun87 EHB> MOVE PmFlags(A1),D2 ; get flags <6Jun87 EHB> CMP #FgColor,D0 ; is this a foreground call? <6Jun87 EHB> BNE.S NotFg ; no => clear PmBkBit <6Jun87 EHB> BCLR #PmFgBit,D2 ; clear the foreground flag bit <6Jun87 EHB> BRA.S Common ; reunite it <6Jun87 EHB> NotFg BCLR #PmBkBit,D2 ; clear the background flag bit <6Jun87 EHB> Common MOVE D2,PmFlags(A1) ; save the flags to GrafVars <6Jun87 EHB> MOVE.L 8(SP),A1 ; restore the pointer to the RGBColor <6Jun87 EHB> NoPM MOVE.L red(A1),red(A0,D1) ; copy red and green components to the port MOVE.W blue(A1),blue(A0,D1) ; and blue too ; make an rgbColor on the stack MOVE.L green(A1),-(SP) ; copy green and blue MOVE.W red(A1),-(SP) ; copy red SUBQ #4,SP ; make room for function return DAF PEA 4(SP) ; push pointer to rgb DAF _Color2Index ; convert it to an index MOVE.L (SP)+,D0 ; get the index result DAF ADDQ #6,SP ; get rid of the rgbColor DAF DONE MOVE.L (SP)+,A0 ; get pointer to index field MOVE.L D0,(A0) ; and set the index field RTD #4 ; all done DAF RGB2OLD ;----------------------------------------------- ; ; UTILITY TO CONVERT AN RGB (POINTED TO BY A1) VALUE ; TO AN OLD STYLE COLOR VALUE. RETURNS VALUE IN D0. CLOBBERS A0,D1 ; ; USES HIGH BIT OF EACH COMPONENT TO SELECT RGB OFF (0) OR ON (1) MOVEQ #0,D1 ; clear out D1 MOVE (A1)+,D1 ; get red ADD.L D1,D1 ; get high bit MOVE (A1)+,D1 ; get green ADD.L D1,D1 ; get high bit MOVE (A1)+,D1 ; get blue ADD.L D1,D1 ; get high bit SWAP D1 ; get RGB index noBlue LEA MapTbl,A0 ; get translate table MOVEQ #0,D0 ; clear out high word MOVE 0(A0,D1*2),D0 ; convert to planar value RTS ; => all done ; TABLE TO MAP FROM 3 BIT RGB TO OLD-STYLE COLOR INDICES MapTBL DC.W blackColor ; RBG = 0,0,0 -> black DC.W blueColor ; RBG = 0,0,1 -> blue DC.W greenColor ; RBG = 0,1,0 -> green DC.W cyanColor ; RBG = 0,1,1 -> cyan DC.W redColor ; RBG = 1,0,0 -> red DC.W magentaColor ; RBG = 1,0,1 -> magenta DC.W yellowColor ; RBG = 1,1,0 -> yellow DC.W whiteColor ; RBG = 1,1,1 -> white ; ; procedure RGBBackColor (Color : RGBColor); ; ; This routine takes an RGB triple and sets the current Graf- or CGrafPort ; fields so that drawing will take place with the best match of the ; requested color, using the current GDevice's color matching rules. The ; appropriate fields are set depending on portType. ; RGBBackColor MOVEQ #BKCOLOR,D0 ; get offset to the index field MOVEQ #RGBBkColor,D1 ; get offset to RGB field BRA.S SHARE ; => and use common code ; ; PROCEDURE GetForeColor (VAR Color: RGBColor); ; ; Return the RGB components of the current foreground color ; Works for old and new ports. GetForeColor PROC EXPORT EXPORT GetBackColor MOVEQ #FGCOLOR,D0 ; get offset to the index field MOVEQ #RGBFgColor,D1 ; get offset to RGB field SHARE MOVE.L 4(SP),A1 ; point at the RGB color MOVE.L GRAFGLOBALS(A5),A0 ; get the QuickDraw globals pointer MOVE.L THEPORT(A0),A0 ; point at the port TST PORTBITS+ROWBYTES(A0) ; is it a new port? BPL.S GETOLD ; => no, set using planar values MOVE.L red(A0,D1),red(A1) ; copy red and green components from the port MOVE.W blue(A0,D1),blue(A1) ; and blue too DONE MOVE.L (SP)+,A0 ; get the return address ADDQ #4,SP ; get rid of the parameter JMP (A0) ; and return ; USE CMYB BITS IN COLOR TO DERIVE PROPER RGB COMPONENTS GETOLD MOVE.L (A0,D0),D0 ; get planar color from port LSR #1,D0 ; CHECK FOR WHITE (I CMYB rgbw) BCS.S @BLACK ; =>NOT WHITE OR #$00E0,D0 ; ELSE SET WHITE BITS (CMY = 111) @BLACK LSR #5,D0 ; SHIFT ICMYÊINTO LOW NIBBLE AND #$7,D0 ; CLEAR ALL BUT CMY MOVE.L QDColors,A0 ; GET DEFAULT COLORS LEA 2(A0,D0*8),A0 ; point to proper entry MOVE.L (A0)+,(A1)+ ; copy red, green MOVE (A0)+,(A1)+ ; copy blue BRA.S DONE ; => all done ; ; PROCEDURE GetBackColor (VAR Color: RGBColor); ; ; Return the RGB components of the current background color ; Works for old and new ports. ; GetBackColor MOVEQ #BKCOLOR,D0 ; get offset to the index field MOVEQ #RGBBkColor,D1 ; get offset to RGB field BRA.S SHARE ; and use common code ; ; function RealColor ( color : RGBColor ) : boolean; ; ; This call tests if the given RGBColor actually exists in the current ; device colortable. It is analogous to RealFont in the Font Manager. ; It tests if the best match to the provided color is actually in the ; device colortable rather than actually testing the color table elements. ; RealColor FUNC EXPORT result EQU 12 inCol EQU 8 I2CSpace EQU -6 LINK A6,#I2CSpace ; build a stack frame DAF CLR.B result(A6) ; assume result false SUBQ #4,SP ; make room for the function return DAF MOVE.L inCol(A6),-(SP) ; push pointer to rgbColor DAF _Color2Index ; find the index, leave result on stack DAF PEA I2CSpace(A6) ; push pointer to rgbColor result DAF _Index2Color ; get the device colorTable entry MOVE.L ([theGDevice]),A0 ; get a pointer to the current device DAF MOVE.L ([GDITable,A0]),A0 ; get the Itable's master pointer DAF MOVE.W ITabRes(A0),D2 ; get the current iTable resolution DAF BFEXTU ([inCol,A6]){0:D2},D0 ; get res hi bits of red input DAF BFEXTU I2CSpace(A6){0:D2},D1 ; get res hi bits of red result DAF CMP D0,D1 ; compare them DAF BNE.S @0 ; if different then return FALSE DAF BFEXTU ([inCol,A6],green){0:D2},D0 ; get res hi bits of grn input DAF BFEXTU I2CSpace+green(A6){0:D2},D1 ; get res hi bits of grn result DAF CMP D0,D1 ; compare them DAF BNE.S @0 ; if different then return FALSE DAF BFEXTU ([inCol,A6],blue){0:D2},D0 ; get res hi bits of blu input DAF BFEXTU I2CSpace+blue(A6){0:D2},D1 ; get res hi bits of blu result DAF CMP D0,D1 ; compare them DAF BNE.S @0 ; if different then return FALSE DAF MOVE.B #1,result(A6) ; the color exists, so set boolean TRUE @0 UNLK A6 ; dump stack frame RTD #4 ; and return DAF ; ; procedure SetEntries ( start,count : integer; aTable : ptr ); ; ; This routine places the specified color into position index in the ; current device color table. The value field of this element is set to ; ownerID, which may include an owner ID number, as well as protect and ; reserve bits. If this value is zero, then the value is considered ; to be fair game for the system. After updating the GDevice data ; structures, the device's driver is called to actually change the ; lookup table. ; SetEntries PROC EXPORT ; rewritten DAF csc_SetEntries EQU 3 ; control call # for setEntries Start EQU 14 Count EQU 12 Tbl EQU 8 SEPBlock EQU -csParam-4 ; parmBlock for control (32 bytes) CParms EQU SEPBlock-8 ; control call parameters (8 bytes) LinkSize EQU CParms LINK A6,#LinkSize ; set up a stack frame MOVEM.L A2/D3,-(SP) ; save some registers ; first, test to make sure the requested entries can be changed MOVE.L ([theGDevice]),A0 ; get the master pointer to current device MOVE.W GDID(A0),D2 ; get theGDevice's ownerID CMP.W #CLUTType,GDType(A0) ; is this a changeable device? BNE SEDevErr ; nope, so quit ; Mark QDSpare that the color environment has changed, but only if ; QD exists TST.B QDExist ; is QD around? BNE.S @noQDE ; no, skip this next MOVE.L (A5),A2 BSET #3,QDSpare0(A2) ; Tell PMgrExit to do color cleaning @noQDE MOVE.L ([GDPMap,A0]),A0 ; get a pointer to the device pixmap MOVE.L ([pmTable,A0]),A2 ; get a pointer to the device's color table MOVE.W Count(A6),D1 ; get the count MOVE.W Start(A6),D0 ; get the starting position BMI.S IndexMode ; if minus, then index mode SeqMode ADD.W D1,D0 ; calc the last entry to change CMP.W CTSize(A2),D0 ; compare to color table size DAF BGT.S RangeErr ; if in legal range (²), continue DAF @0 BTST #protectBit,CTTable+value(A2,D0*8) ; test the protect bit DAF BNE.S ProtError ; oh, oh. Can't changed a protected one! BTST #ReserveBit,CTTable+value(A2,D0*8) ; test the reserve bit DAF BEQ.S @1 ; if clear, then it can be changed DAF CMP.B CTTable+value+1(A2,D0*8),D2 ; compare to current ClientID (byte only) DAF BNE.S ProtError ; sorry, can't change someone else's DAF @1 SUBQ #1,D0 ; back index up by one DBRA D1,@0 ; loop back BRA.S OKFine ; the request is valid RangeErr MOVE #cRangeErr,D0 ; if not, then return an error BRA SEError ; ProtError MOVE #cProtectErr,D0 ; BRA SEError IndexMode ; test the .value fields in INDEX mode MOVE.L Tbl(A6),A0 ; get addr of first request @0 MOVE.W value(A0),D0 ; get the destination location from request array AND.W #$00FF,D0 ; lo-byte only for index CMP.W CTSize(A2),D0 ; is the request in range? DAF BGT.S RangeErr ; nope, so quit DAF BTST #protectBit,CTTable+value(A2,D0*8) ; test the protect bit DAF BNE.S ProtError ; oh, oh. Can't changed a protected one! BTST #ReserveBit,CTTable+value(A2,D0*8) ; test the reserve bit DAF BEQ.S @1 ; if clear, then it can be changed DAF CMP.B CTTable+value+1(A2,D0*8),D2 ; compare to current ClientID (byte only) DAF BNE.S ProtError ; sorry, can't change someone else's DAF @1 ADDQ #CTEntrySize,A0 ; move to next entry DBRA D1,@0 ; reduce count OKFine MOVE.W Start(A6),CParms+csStart(A6) ; set up control parm block MOVE.W Count(A6),CParms+csCount(A6) ; MOVE.L Tbl(A6),CParms+csTable(A6) ; ; now build a parameter block for a control call (in stack frame) MOVE.L #0,SEPBlock+ioCompletion(A6) ; no completion routine MOVE.W #0,SEPBlock+ioVRefNum(A6) ; device not volume MOVE.L ([theGDevice]),A1 ; get ptr to theGDevice MOVE.W (A1),SEPBlock+ioRefNum(A6) ; copy refNum from theGDevice MOVE.W #csc_SetEntries,SEPBlock+csCode(A6) ; control code for this call LEA CParms(A6),A1 ; get addr of parms MOVE.L A1,SEPBlock+csParam(A6) ; move addr of parms into block ; make a call to the device driver LEA SEPBlock(A6),A0 ; get pointer to parmblock _Control ,IMMED ; make a control call DAF ; test result and finish up TST.W D0 ; test the result BNE.S SEError ; put the RGBs into theGDevice's colortable. Copy the current ownerID into .value ; (wait until control call returns successfully!). D2 has GDID in it from above MOVE.L Tbl(A6),A0 ; get the request array ptr MOVE.W Count(A6),D0 ; get the number of entries to install MOVE.W Start(A6),D1 ; get the starting position number BMI.S InstIndex ; if minus, then INDEX mode InstSeq LEA CTTable(A2,D1*8),A1 ; get ptr to start position in sysColorTable @0 ADDQ #1,A1 ; advance to lo half of value DAF MOVE.B D2,(A1)+ ; set value field to ownerID DAF MOVE.L (A0)+,D3 ; get request value (junk) and red MOVE.W D3,(A1)+ ; set red MOVE.L (A0)+,(A1)+ ; set green and blue DBRA D0,@0 ; loop for all requests BRA.S FinishUp ; InstIndex MOVE.W (A0)+,D3 ; get the insertion index (.value field) MOVE.B D2,CTTable+value+1(A2,D3*8) ; set value field to the ownerID DAF MOVE.W (A0)+,CTTable+rgb+red(A2,D3*8) ; set red MOVE.L (A0)+,CTTable+rgb+green(A2,D3*8) ; set green and blue DBRA D0,InstIndex ; loop for all entries FinishUp ; update the CTSeed CLR.L -(SP) ; make room for function return _rGetCTSeed ; get the next seed MOVE.L (SP)+,CTSeed(A2) ; and put it in the table ; invalidate the cached fonts MOVE.L MinusOne,lastSPExtra ; this marks them invalid DAF SEDone MOVEM.L (SP)+,A2/D3 ; restore saved registers UNLK A6 ; dump stack frame RTD #8 SEDevErr MOVE.W #cDevErr,D0 ; wrong device type error SEError MOVE.W D0,QDErr ; record the error code DAF BRA.S SEDone ; and finish up DAF ; ; procedure RestoreEntries (srcTable:cTabHandle; DstTable:cTabHandle; var selection:requestList); ; ; RestoreEntries sets a selection of entries from srcTable into DstTable. The dstTable elements ; to be set are enumerated in selection (these values are offsets into the srcTable, ; not the contents of the value field). If a request is beyond the end of the dstTable, ; that position of the requestList is set to invalColReq, and is not installed into dstTable. ; This call does not observe the reserve and protect fields of entries in dstTable. ; If DstTable is NIL (or = to theGDevice's colortable), then srcTable is installed into ; theGDevice's colortable, and the CLUT is set to the new values. Note that srcTable and ; selection are assumed to have the same number of elements. ; RestoreEntries PROC EXPORT csc_SetEntries EQU 3 ; control call # for setEntries ; stack frame declarations SrcT EQU 16 dstT EQU 12 RLst EQU 8 SEPBlock EQU -csParam-4 ; parmBlock for control (36 bytes) CParms EQU SEPBlock-8 ; control call parameters (8 bytes) LinkSize EQU CParms ; size of stack frame locals LINK A6,#LinkSize ; MOVEM.L A2/A3/D3,-(SP) ; save some registers CLR.W QDErr ; clear error register MOVE.L ([SrcT,A6]),A2 ; get source colortable ptr MOVE.L RLst(A6),A1 ; get the request list ptr MOVE.W ReqLSize(A1),D2 ; get the request list size MOVE.L ([theGDevice]),A0 ; get a pointer to the current device CMP.W #CLUTType,GDType(A0) ; is it a clut device? BNE REDevErr ; nope, so report an error MOVE.L ([GDPMap,A0]),A0 ; get pixMap's handle DAF MOVE.L ([PMTable,A0]),A0 ; get a pointer to the device colortable DAF MOVE.L ([dstT,A6]),A3 ; get destination colortable ptr in case it's OK CMP.L A0,A3 ; does dest point to theGDevice's colortable? BEQ.S @8 ; so do the control call TST.L dstT(A6) ; is destination NIL? BNE.S JustInstall ; if non-NIL, then just install the srcTbl MOVE.L A0,A3 ; move the device colortable ptr to A3 @8 ; if DstTbl was NIL or (ptr) equal, then dest is theGDevice's colortable... ; save the value fields of the srcTable on the stack MOVEQ #0,D1 ; clear hi half of D0 MOVE.W D2,D1 ; make a copy of the srcTable size ADDQ #1,D1 ; adjust from count to size ASL #1,D1 ; times two for words SUBA D1,SP ; make room for these values on stack MOVE.W D2,D0 ; get # of value fields to move @0 MOVE.W CTTable+value(A2,D0*8),(SP,D0*2) ; copy the values DBRA D0,@0 ; ; now install the position fields into the srcTable MOVE.W D2,D0 ; get # of value fields to move @1 MOVE.W ReqLData(A1,D0*2),D3 ; get the target position CMP.W CTSize(A3),D3 ; compare to the maximum for this destination BGT LeakError ; if out of range, return error (Bruce wanted this) MOVE.W D3,CTTable+value(A2,D0*8) ; write the request in the source table DBRA D0,@1 ; ; at this point, the "bad" entries should be removed, but we can leave them ; for now since the driver will range test positions (and invalColReq >256). ; make a driver call with the modified src table MOVE.L #0,SEPBlock+ioCompletion(A6) ; no completion routine MOVE.W #0,SEPBlock+ioVRefNum(A6) ; device not volume MOVE.L ([theGDevice]),A0 ; get ptr to theGDevice MOVE.W (A0),SEPBlock+ioRefNum(A6) ; copy refNum from theGDevice MOVE.W #csc_SetEntries,SEPBlock+csCode(A6) ; control code for this call LEA CParms(A6),A0 ; get addr of parms MOVE.L A0,SEPBlock+csParam(A6) ; move addr of parms into block ; set up control call parameters LEA CTTable(A2),A0 ; get pointer to the colorspecs MOVE.L A0,cParms+csTable(A6) ; put in parameter block MOVE.W #-1,cParms+csStart(A6) ; always use index mode MOVE.W D2,cParms+csCount(A6) ; for the correct size ; make a call to the device driver LEA SEPBlock(A6),A0 ; get pointer to parmblock _Control ,IMMED ; make a control call DAF ; test result and finish up BEQ.S ReleaseSP ; call went ok, so continue MOVE.W D0,QDErr ; report the control code error ; finally, restore the src table value fields ReleaseSP MOVE.W D2,D0 ; get # of value fields to move @2 MOVE.W (SP,D0*2),CTTable+value(A2,D0*8) ; copy the values DBRA D0,@2 ; ADDA D1,SP ; release the temporary space JustInstall MOVE.W ReqLData(A1,D2*2),D0 ; get the request index CMP.W #invalColReq,D0 ; if the request is bad, skip it BEQ.S JLoopCont ; CMP.W CTSize(A3),D0 ; is it in the legal range? BGT.S JIErr ; if not, then return an error MOVE.L CTTable(A2,D2*8),CTTable(A3,D0*8) ; copy value and red MOVE.L CTTable+rgb+green(A2,D2*8),CTTable+rgb+green(A3,D0*8) ;copy green and blue JLoopCont DBRA D2,JustInstall ; loop back Exeunt TST.L dstT(A6) ; if NIL, was a device call, so call the Palette Mgr BNE.S Moofem ; nope, so skip around MOVE.L RLst(A6),-(SP) ; push the request list pointer MOVE #18,D0 ; this is the selector for the _ReleaseList call DC.W $AAA2 ; this is the extensible PaletteMgr trap Moofem MOVEM.L (SP)+,A2/A3/D3 ; restore work registers UNLK A6 ; release stack frame RTD #12 ; and return to caller JIErr MOVE.W #cRangeErr,QDErr ; move error message into QDErr BRA.S JLoopCont ; continue REDevErr MOVE.W #cDevErr,QDErr ; BRA.S Moofem ; ; If ANY of the requests are out of range, then leave the whole call without doing anything. Bruce ; asked for this change since ancient users of Save/RestoreEntries (PixelPaint and Color Picker) ; will trash the system with RestoreEntries if they were Switcheroo'd into a different depth. This ; ONLY protects you if you are switching to a lower pixel depth, but it helps out a lot in those ; cases LeakError MOVE.W #cRangeErr,QDErr ; report an error MOVE.W D2,D0 ; get # of value fields to move @2 MOVE.W (SP,D0*2),CTTable+value(A2,D0*8) ; restore the sourcetable values DBRA D0,@2 ; ADDA D1,SP ; release the temporary space BRA.S Exeunt ; and out ; ; procedure SaveEntries (srcTable:cTabHandle; ResultTable:cTabHandle; var selection:requestList); ; ; SaveEntries saves a selection of entries from srcTable into ResultTable. The elements ; to be copied are enumerated in selection (these values are offsets into the colorTable, ; not the contents of the value field). If an element is not present in srcTable, then ; that position of the requestList is set to invalColReq, and that position of ResultTable has random ; values returned. If one or more elements are not found, then an error code is posted ; to QDErr; however, for every element in selection which is not -1, the values in ResultTable ; are valid. Note that srcTable and selection are assumed to have the same number ; of elements. ; SaveEntries PROC EXPORT SrcT EQU 16 ResT EQU 12 RLst EQU 8 LINK A6,#0 ; MOVE.L A2,-(SP) ; save a register CLR.W QDErr ; clear error register TST.L SrcT(A6) ; if source is NIL, use theGDevice's ctable BNE.S @80 ; MOVE.L ([theGDevice]),A0 ; get a pointer to the current device CMP.W #CLUTType,GDType(A0) ; is it a CLUT device? <1.4> BNE.S SavEntDevErr ; nope, so report an error <1.4> MOVE.L ([GDPMap,A0]),A0 ; get pixMap's handle DAF MOVE.L ([PMTable,A0]),A2 ; get a pointer to the device colortable DAF BRA.S @81 ; and continue @80 MOVE.L ([SrcT,A6]),A2 ; get source colortable ptr @81 MOVE.L RLst(A6),A1 ; get the request list ptr MOVE.W ReqLSize(A1),D2 ; get the request list size MOVE.W D2,D0 ; copy size to a temp register ADDQ #1,D0 ; increase by one for size calculations MULU #CTEntrySize,D0 ; calc the result table size ADDQ #CTTable,D0 ; add the header size MOVE.L ResT(A6),A0 ; get the result table handle TST.L (A0) ; is the master pointer NIL? BEQ.S @0 ; if so, do a realloc _SetHandleSize ; set the result table size BRA.S @1 ; @0 _ReallocHandle ; @1 BEQ.S @2 MOVE.W #CNoMemErr,QDErr ; mark the error DAF BRA.S SaveEntDone ; and quit DAF @2 MOVE.L (A0),A0 ; get a pointer to the result table MOVE.L #0,CTSeed(A0) ; clear CTSeed MOVE.W #0,TransIndex(A0) ; clear transparent value MOVE.W D2,CTSize(A0) ; #elements = size of request table SavLoop MOVE.W ReqLData(A1,D2*2),D0 ; get the request value CMP.W CTSize(A2),D0 ; is it in the allowable range? DAF BGT.S SaveEntRangeErr ; no, so mark an error MOVE.L CTTable(A2,D0*8),CTTable(A0,D2*8) ;move value and red to result table MOVE.L CTTable+rgb+green(A2,D0*8),CTTable+rgb+green(A0,D2*8) ; green and blue SLoopCont DBRA D2,SavLoop ; go on to the next request SaveEntDone ; MOVE.L (SP)+,A2 ; restore that register UNLK A6 ; trash the stack frame RTD #12 ; and return SaveEntRangeErr ; MOVE.W #invalColReq,ReqLData(A1,D2*2) ; mark this element invalid MOVE.W #cRangeErr,QDErr ; move error message into QDErr BRA.S SLoopCont ; continue SavEntDevErr ; <1.4><1.5> MOVE.W #cDevErr,QDErr ; post error code <1.4> BRA.S SaveEntDone ; and quit <1.4> ; ; procedure ProtectEntry (index : integer; protect : boolean); ; ; ProtectEntry sets the protect bit in theGDevice's colortable. If ; protect is TRUE, then the bit is set and SetEntry will not change ; it. If protect is FALSE, then the bit is cleared and the CLUT can ; be changed. ; ProtectEntry PROC EXPORT EXPORT ReserveEntry MOVEQ #protectBit,D2 ; this call modifies the protect bit BSR.S CTCommon ; do it OneQuit ; DAF RTD #4 ; ProtErr MOVE.W #cProtectErr,QDErr ; mark the error DAF BRA.S RErrQ ; jump to common exit DAF RangeErr MOVE.W #cRangeErr,QDErr ; mark the error DAF RErrQ ADDQ #4,SP ; kill the BSR's return addr DAF BRA.S OneQuit ; use ProtectEntry's (both have same cleanup) DAF ; ; utility CTCommon ; ; CTCommon is a utility shared by ProtectEntry and Reserve entry. It ; sets/clears the bit (whose mod 8 position is in D2) in theGDevice's ; colortable. It trashes A1/D1 and ReserveEntry expects A0 to contain ; a pointer to the colorTable and D0 to be the affected index. ; Corrected offsets into stack frame and scaling (C575) CTCommon CLR.W QDErr ; clear the error flag DAF MOVE.L ([theGDevice]),A0 ; get the current device pointer MOVE.L ([GDPMap,A0]),A0 ; get the device pixelmap pointer MOVE.L ([pmTable,A0]),A0 ; point to the color table MOVE.W 10(SP),D0 ; get the index DAF CMP.W CTSize(A0),D0 ; is it in the legal range? DAF BGT.S RangeErr ; oops, out of range DAF TST.B 8(SP) ; test the boolean DAF BEQ.S @1 ; if equal, then clear bit BTST D2,CTTable+value(A0,D0.W*8) ; if bit already on? DAF BNE.S ProtErr ; if so, return error DAF BSET D2,CTTable+value(A0,D0.W*8) ; turn bit on DAF BRA.S @2 ; @1 BCLR D2,CTTable+value(A0,D0.W*8) ; turn bit off DAF @2 RTS ; ; procedure ReserveEntry (index : integer; reserve : boolean); ; ; ReserveEntry sets the reserve bit in theGDevice's colortable. If ; reserve is TRUE, then this CLUT element will not be seeded during ; ITable building and will not be a treated as an available color. ; Additionally, if reserve is TRUE, the value field will be marked ; with the current GDID (for symmetry with SetEntries). ; If reserve is FALSE, then the bit is cleared and this entry ; can be a match. The clientID of this entry is not affected ; ReserveEntry MOVEQ #ReserveBit,D2 ; this call modifies the reserve bit BSR.S CTCommon ; do it TST.B 4(SP) ; is reserve true? DAF BEQ.S @1 ; if false, then done DAF MOVE.W CTTable+value(A0,D0.W*8),D1 ; get value field of affected colorSpec DAF MOVE.L ([theGDevice]),A1 ; get pointer to theGDevice DAF MOVE.B GDID+1(A1),CTTable+value+1(A0,D0.W*8) ; set clientID (lo byte of GDID) DAF @1 CLR.L -(SP) ; make room for function return DAF _rGetCTSeed ; get the next seed MOVE.L (SP)+,CTSeed(A0) ; and put it in the table DAF RTD #4 ; return to caller ; ; procedure SetClientID ( id : integer ); ; ; SetClientID updates the clientID field of theGDevice. This field is ; used by the search and complement procedures to determine if they ; should operate on a particular request. SetClientID PROC EXPORT MOVE.L ([theGDevice]),A0 ; get the current device pointer MOVE.W 4(SP),GDID(A0) ; set it RTD #2 ; return ; ; procedure AddSearch (SearchProc : ProcPtr); ; ; This routine adds a search procedure to the head of the list of ; searchprocs in theGDevice. ; AddSearch PROC EXPORT EXPORT AddComp CLR.W QDErr ; clear the error register DAF MOVE.L (SP)+,D2 ; grab the return address MOVE.L (SP)+,D1 ; grab the searchProc pointer BEQ.S AllDone ; if NIL, don't install MOVE.L #sProcSize,D0 ; allocate a new proc element _NewHandle ; get that memory DAF BNE.S ProcMemFailure ; couldn't get it move.l d1,d0 ; get procPtr in d0 _rTranslate24to32 ; clean up the address MOVE.L ([theGDevice]),A1 ; get the current device pointer MOVE.L GDSearchProc(A1),D1 ; hold the current search proclist element MOVE.L A0,GDSearchProc(A1) ; install the new element MOVE.L (A0),A0 ; get the procList pointer DAF MOVE.L D0,SRCHPROC(A0) ; put in the list DAF MOVE.L D1,NXTSRCH(A0) ; link up chain DAF ; Get a unique seed for this device to force the search proc to get called and the Itab rebuilt ; ; Removed for PhotoMac compatibility ; MOVE.L ([GDPMap,A1]),A1 ; get the device pixelmap pointer ; MOVE.L pmTable(A1),d0 ; get the device clut handle ; beq.s AllDone ; skip if no clut ; move.l d0,a1 ; MOVE.L (a1),A1 ; get the device clut pointer ; CLR.L -(SP) ; make room for function return ; _rGetCTSeed ; get the next seed ; MOVE.L (SP)+,CTSeed(A1) ; and put it in the table AllDone JMP (ZA0,D2.L) ; return home ProcMemFailure MOVE.W #cNoMemErr,QDErr ; load memory error code DAF BRA.S AllDone ; DAF ; ; procedure AddComp (CompProc : ProcPtr); ; ; This routine adds a complement procedure to the head of the list of ; compprocs in theGDevice. ; AddComp CLR.W QDErr ; clear the error register DAF MOVE.L (SP)+,D2 ; grab the return address MOVE.L (SP)+,D1 ; grab the compProc pointer BEQ.S AllDunn ; if NIL, don't install MOVE.L #sProcSize,D0 ; allocate a new proc element _NewHandle ; get that memory DAF BNE.S ProcMemFailure ; couldn't get it move.l d1,d0 ; get procPtr in d0 _rTranslate24to32 ; clean up the address MOVE.L ([theGDevice]),A1 ; get the current device pointer MOVE.L GDCompProc(A1),D1 ; hold the current complement proclist element DAF MOVE.L A0,GDCompProc(A1) ; install the new element MOVE.L (A0),A0 ; get procList pointer DAF MOVE.L D0,COMPPROC(A0) ; put in the list DAF MOVE.L D1,NXTCOMP(A0) ; link up chain DAF AllDunn JMP (ZA0,D2.L) ; return home ; ; procedure DelSearch ( SearchProc : ProcPtr ); ; ; This routine removes a search proc (identified by it's procPtr from theGDevice's search list ; ; contains fix from QDciPatchROM.a to clean up the address before searching stb DelSearch PROC EXPORT ; DAF MOVE.L 4(SP),D0 ; get the SearchProc pointer _rTranslate24to32 ; clean up the address MOVE.L ([theGDevice]),A1 ; get the current device pointer ; Get a unique seed for this device to force the Itab to get rebuilt ; Removed for PhotoMac compatibility ; MOVE.L ([GDPMap,A1]),A0 ; get the device pixelmap pointer ; MOVE.L pmTable(A0),d0 ; get the device clut handle ; beq.s skipSeed ; skip if no clut ; move.l d0,a0 ; MOVE.L (a0),A0 ; get the device clut pointer ; CLR.L -(SP) ; make room for function return ; _rGetCTSeed ; get the next seed ; MOVE.L (SP)+,CTSeed(A0) ; and put it in the table skipSeed LEA GDSearchProc(A1),A1 ; get pointer to "nxtSearch" field MOVE.L (A1),D2 ; hold the current search proclist handle @1 BEQ.S DSrchDone ; if nil, then end of chain MOVE.L D2,A0 ; copy to an addr register MOVE.L (A0),A0 ; get the pointer to proc list elem CMP.L srchProc(A0),D0 ; do they match? BEQ.S RmvSrch ; yes, so splice it out LEA NxtSrch(A0),A1 ; get addr of this NxtSrch field MOVE.L NxtSrch(A0),D2 ; go to the next one BRA.S @1 RmvSrch MOVE.L NxtSrch(A0),(A1) ; relink the chain around this element MOVE.L D2,A0 ; setup for Dispose _DisposHandle ; dispose the procLstElem DSrchDone RTD #4 ; strip params and return ; ; procedure DelComp ( complementProc : ProcPtr ); ; ; This routine removes a complement proc (identified by it's procPtr from theGDevice's search list ; ; contains fix from QDciPatchROM.a to clean up the address before searching stb DelComp PROC EXPORT ; DAF MOVE.L 4(SP),D0 ; get the CompProc pointer _rTranslate24to32 ; clean up the address MOVE.L ([theGDevice]),A1 ; get the current device pointer LEA GDCompProc(A1),A1 ; get pointer to "nxtComp" field MOVE.L (A1),D2 ; hold the current complement proclist handle @1 BEQ.S DCompDone ; if nil, then end of chain MOVE.L D2,A0 ; copy to an addr register MOVE.L (A0),A0 ; get the pointer to proc list elem CMP.L CompProc(A0),D0 ; do they match? BEQ.S RmvComp ; yes, so splice it out LEA NxtComp(A0),A1 ; get addr of this NxtSrch field MOVE.L NxtComp(A0),D2 ; go to the next one BRA.S @1 RmvComp MOVE.L NxtComp(A0),(A1) ; relink the chain around this element MOVE.L D2,A0 ; setup for Dispose _DisposHandle ; dispose the procLstElem DCompDone RTD #4 ; strip params and return ; ; procedure GetSubTable (VAR myColors:CTabHandle; ; iTabRes:integer; ; TargetTbl:CTabHandle); ; ; This routine takes a colorTable pointed at by myColors, and determines ; the best match index values for each rgb in myColors. These best ; matches are returned in the .value fields of myColor's colorSpecs. ; The values returned are best matches to the RGBs in TargetTbl and ; the returned indices are TargetTbl's .values. Best matches are ; calculated using Color2Index and all applicable rules apply. ; iTabRes controls the channel size of the iTable that is built. ; If TargetTbl is NIL, then theGDevice's colortable is used as the target ; and no inverse table is built. ; GetSubTable PROC EXPORT ; offsets to parameters ; offsets corrected DAF myColors EQU 14 ; iTabResolution EQU 12 ; TargetTbl EQU 8 ; ITHndl EQU -4 ; temporary handle for iTable SavHndl EQU -8 ; save theGDevice's iTable here SavCol EQU -12 ; save theGDevice's colortable here LinkSize EQU SavCol ; LINK A6,#LinkSize ; make a stack frame MOVEM.L A2/D3,-(SP) ; save some registers DAF TST.L TargetTbl(A6) ; is target table NIL? BEQ.S @UseOldTable ; nope, so make an ITable _NewEmptyHandle ; make a temp handle for iTable MOVE.L A0,ITHndl(A6) ; save the handle MOVE.L TargetTbl(A6),-(SP) ; push the source cTable MOVE.L A0,-(SP) ; push the iTable handle MOVE.W iTabResolution(A6),-(SP) ; push the resolution _MakeITable ; make an inverse table TST.W QDErr ; did it fail? DAF BNE.S @BailOut ; clean up an quit leaving the QDErr DAF MOVE.L ([theGDevice]),A1 ; get a pointer to the device port MOVE.L GDITable(A1),SavHndl(A6) ; save the current iTable here MOVE.L ITHndl(A6),GDITable(A1) ; and substitute this iTable MOVE.L ([GDPMap,A1]),A1 ; get pixMap's pointer DAF MOVE.L PMTable(A1),SavCol(A6) ; save the current colorTable here MOVE.L myColors(A6),PMTable(A1) ; and substitute this colorTable MOVE.L ([PMTable,A1]),A1 ; copy colorTable's ctSeed into iTable DAF MOVE.L ctSeed(A1),D0 ; get the ctSeed (to avoid rebuilds) DAF MOVE.L ([ITHndl,A6]),A1 ; copy seed into the temp inverseTable DAF MOVE.L D0,ITabSeed(A1) ; put seed into the iTable DAF @UseOldTable MOVE.L myColors(A6),A0 ; get source's cTabHndl _HLock ; just for security DAF MOVE.L (A0),A2 ; get a pointer DAF MOVE.W CTSize(A2),D3 ; get the number of entries DAF @0 SUBQ #4,SP ; make room for function return DAF PEA CTTable+rgb(A2,D3*8) ; push the entry's rgb addr DAF _Color2Index ; get the value ADDQ #2,SP ; throw away hi word of result DAF MOVE.W (SP)+,CTTable+value(A2,D3*8) ; place result in table DAF DBRA D3,@0 ; loop on all entries MOVE.L myColors(A6),A0 ; get source's cTabHndl DAF _HUnlock ; and free it DAF TST.L TargetTbl(A6) ; was it NIL? BEQ.S @1 ; if it was, then skip the restorations MOVE.L ([theGDevice]),A1 ; get a pointer to the device port MOVE.L SavHndl(A6),GDITable(A1) ; restore the device inverse table MOVE.L ([GDPMap,A1]),A1 ; get pixMap's pointer DAF MOVE.L SavCol(A6),PMTable(A1) ; and substitute this colorTable @BailOut MOVE.L ITHndl(A6),A0 ; get the temp iTabHndl again _DisposHandle ; all done, so dispose @1 MOVEM.L (SP)+,A2/D3 ; restore saved registers DAF UNLK A6 ; release stack frame RTD #10 ; return DAF ; ; function QDError : integer; DAF ; ; This function returns the current value of QDErr, which reflects error conditions ; following the previous QuickDraw/Color Manager call. ; QDError PROC EXPORT MOVE.W QDErr,4(SP) ; move error code to function return CLR.W QDErr ; and clear the code RTS ;--------------------------------------------------- ; ; FUNCTION DeltaRGB(ColorInfo,ColorSpec:Ptr):integer; LOCAL; ; DeltaRGB FUNC EXPORT ParamSize equ 8 ; total bytes of params Delta equ ParamSIze+10-2 ; integer result RGB1 equ Delta-4 ; palette entry pointer RGB2 equ RGB1-4 ; color table entry pointer VarSize equ 0 ; size of variables Link A6,#VarSize ; build stack frame MOVEM.L A0-A1/D0-D1,-(SP) Move.L RGB1(A6),A0 ; get first RGBColor Move.L RGB2(A6),A1 ; get second RGBColor Move (A1)+,D0 ; grab index red Sub (A0)+,D0 ; subtract entry red Bhs.S NotNeg1 ; take abs(Index.Red-ColorInfo.Red) Neg D0 ; negate it NotNeg1 Move (A1)+,D1 ; grab index green Sub (A0)+,D1 ; subtract entry green Bhs.S NotNeg2 ; take abs(Index.Green-ColorInfo.Green) Neg D1 ; negate it NotNeg2 Cmp D0,D1 ; leave greater unsigned value in D0 Blo.S DontSwitch1 ; D1 < D0 => D0 is already greater Move D1,D0 ; green value is greater DontSwitch1 Move (A1),D1 ; grab index blue Sub (A0),D1 ; subtract entry blue Bhs.S NotNeg3 ; take abs(Index.Blue-ColorInfo.Blue) Neg D1 ; negate it NotNeg3 Cmp D0,D1 ; leave greater unsigned value in D0 Blo.S FixResult ; D1 < D0 => D0 is already greater Move D1,D0 ; blue value is greater FixResult Move D0,Delta(A6) ; set result MOVEM.L (SP)+,A0-A1/D0-D1 Unlk A6 ; clear stack frame Rtd #ParamSize ; strip parameters and go home ENDF ;--------------------------------------------------- ; ; FUNCTION myGetCTable(ID:INTEGER):Handle; ; ; Get a 'clut' resource of the specified ID, as a non-resource ; handle. Unless, of course, the number is 32+[1,2,4,8] in which ; case we return a gray scale of that size. Or if the number is ; 64+[1,2,4,8] we'll return a clut with a hilite modification. ; ; Passing 64 plus other than 1, 2, 4, or 8 will yield strange results. ; ; stack like this: (SP) return.L, 4(SP) depth.W, 6(SP) result handle.L ; ; for whole routine PROC EXPORT GetCTable GrayMuls DC.W $FFFF,$5555,$0000,$1111,$0000,$0000,$0000,$0101 ; ; gray multiplier 1-bit 2-bit 4-bit 8-bit doGrayClut AND #$F,D0 ; Mask the graybit MOVE D0,4(SP) ; Put it in the passed param CLR.L D1 ADDQ #3,D0 ; + 3 becomes * 8 on next line, for c-Spec size BSET D0,D1 ; 2 to the nth ADDQ.L #8,D1 ; Add size of ctab header MOVE.L D1,D0 ; Here, for newhandle _NewHandle ; Get a brand new colortable BEQ.S @gotGrayH ; Did we? CLR.L 6(SP) ; No=>return nil RTD #2 @gotGrayH MOVE 4(SP),D0 ; Get depth again LEA GrayMuls-2,A1 ; A1->our gray multiplier table <2.1> MOVE.w (A1,D0*2),D1 ; D1.w = color step for this bit depth move.w d1,d2 ; copy into d2 swap d2 ; set up high word move.w d1,d2 ; replicate throughout long MOVE.L (A0),A1 ; A1->our brand new ctable clut move.w #$8000,transIndex(A1) ; mark as device clut CLR D1 ; From bit depth, BSET D0,D1 ; Generate number of entries SUBQ #1,D1 ; Minus one, for ctSize silliness MOVE D1,ctSize(A1) ; Set size of colortable CMPI #3,D0 ; Size 1 or 2 bits? BLT.S @graySeed ; Yes => don't call it GrayScale by seed ADD #32,D0 ; Constant +32 for GrayScale seeds @graySeed MOVE.L D0,ctSeed(A1) ; Jam seed LEA ctTable(A1),A1 ; A1->first colorSpec MOVEQ #-1,D0 ; Lowest index is white MOVEQ #0,D1 ; D1 will be .value fields @grayLoop MOVE D1,(A1)+ ; put pixel value in .value field ADDQ #1,D1 ; MOVE.L D0,(A1)+ ; Stash red and green MOVE D0,(A1)+ ; Stash blue, A1->next cSpec SUB.L D2,D0 ; Bump D0 to next reasonable value BCC.S @grayLoop ; Until we go under zero, beyond black BRA GoHome doHiliteClut AND #$F,D0 ; Mask the hilitebit MOVE D0,-(SP) ; Push it for the Resource read BSR ReadIt ; Okay, read it. MOVE.L A0,D0 ; A good thing? BEQ GoHome ; No=>go home (with NIL from A0) MOVE.L (A0),A1 ; A1->color table SUBQ #4,SP ; Space for new seed _GetCTSeed ; Get that new seed MOVE.L (SP)+,ctSeed(A1) ; Put seed into synthetic table MOVE 4(SP),D0 ; D0 = clutID again BTST #1,D0 ; Two bit mode? BEQ.S checkFour ; No=>check other stuff ; If the hilite color is a gray then CLUT is OK <2.2> LEA HiliteRGB,A1 ; Point at hilite color <2.2> MOVE.B red(A1),D1 ; Get red msb <2.2> CMP.B green(A1),D1 ; green same as red? <2.2> BNE.S @notGray ; no, go use it. <2.2> CMP.B blue(A1),D1 ; blue same as red and green? <2.2> BEQ GoHome ; yes, leave clut alone. <2.2> @notGray MOVE.L (A0),A1 ; A1->color table <2.2> LEA ctTable+8+rgb(A1),A1 ; Bump A1 to 2nd entry.rgb.red MOVE.L #$7FFF7FFF,(A1)+ ; Put 50% gray into red,green MOVE #$7FFF,(A1)+ ; and blue ADDQ #2,A1 ; Bump past .value field in third entry MOVE.L HiliteRGB,(A1)+ ; Put hilite color into red,green MOVE HiliteRGB+blue,(A1) ; and blue. BRA GoHome checkFour BTST #2,D0 ; Four bit mode? BEQ GoHome ; No => must be 1 or 8-bit, so we're done. MOVE #14,D2 ; DBRA counter, skipping white MOVE.L #$0001FFFF,D1 ; Lo word = error distance, Hi word = best index @B4loop SUBQ #2,SP ; space for result PEA HiliteRGB ; push system hilite color PEA ctTable+rgb(A1,D2*8) ; push color we're checking JSR DeltaRGB MOVE (SP)+,D0 CMP D0,D1 BLS.S @B4loopEnd MOVE D0,D1 ; Get new smallest delta SWAP D1 ; D1 = Get index number in low word MOVE D2,D1 ; Get new closest index SWAP D1 ; D1 = Put delta back to low word @B4loopEnd SUBQ #1,D2 BNE.S @B4loop ; Loop on down, but skip zero CMPI #$3000,D1 ; Are we pretty darned tolerant? BLS.S GoHome ; Yes=> we're fine, go home SWAP D1 LEA ctTable+rgb+red(A1,D1*8),A1 ; A1->RGB to tweak @realClose MOVE.L HiliteRGB+red,D0 ; D0 = hilite r,g SWAP D0 ; D0.W = red ADD red(A1),D0 ; D0.W = hilite red+old red ROXR #1,D0 ; Average MOVE D0,red(A1) ; write red SWAP D0 ADD green(A1),D0 ; D0.W = hilite green+old green ROXR #1,D0 ; Average MOVE D0,green(A1) ; write green MOVE HiliteRGB+blue,D0 ; D0.W = hilite blue ADD blue(A1),D0 ROXR #1,D0 ; Average MOVE D0,blue(A1) ; write blue SUBQ #2,SP ; space for result PEA HiliteRGB ; Have we really got the hilite PEA (A1) ; color within a reasonable range? JSR DeltaRGB ; Let's see. CMPI #$3000,(SP)+ ; 3000 is good enough to look decent. BHI.S @realClose ; But, its not there yet, so avg again. BRA.S GoHome GetCTable clr.l 6(sp) ; NIL return if call fails. MOVE 4(SP),-(SP) ; First, try to read a resource BSR.S ReadIt ; (do that) MOVE.L A0,D0 ; Get anything? BEQ.S notRes ; No=>try other means GoHome MOVE.L A0,6(SP) ; Yes=>stash it RTD2 RTD #2 ; roger and out. notRes MOVE 4(SP),D0 ; Get requested ID CMPI #72,D0 ; 72 is the highest we can noodle BHI.S RTD2 MOVE.L #$00000116,D1 ; Bits 1, 2, 4, and 8 are set BTST D0,D1 ; Are low 5 bits in [1,2,4,8]? BEQ.S RTD2 BTST #5,D0 BNE.S doGrayClut ; 32+[1,2,4,8] BTST #6,D0 BNE.S doHiliteClut ; 64+[1,2,4,8] BRA.S RTD2 readIt MOVEM.L D6/D7,-(SP) ; Save some work registers MOVE.W #mapTrue,ROMMapInsert ; Put the ROM map at the lend of the list SUBQ #4,A7 ; space for result MOVE.L #'clut',-(SP) ; resource type MOVE 20(SP),-(SP) ; push ID number _GetResource ; Get it. MOVE.L (SP)+,D7 ; Did we get the resource? BEQ.S @readFail ; -> Nope, Clear A0 and exit. MOVE.L D7,-(SP) ; Push the Resource Handle _DetachResource ; Free it from the Map MOVE.L D7,D6 ; Put the orig in D6 MOVE.L D7,A0 ; Get the handle in A0 MOVE.L D7,A1 ; Get the handle in A1 MOVE.L (A1),A1 ; Get the ptr CMP.L ROMBase,A1 ; Is the clut in ROM? BLO.S @SetTheSeed ; -> No. Its in RAM. Set the seed and leave _HandToHand ; Make a copy in a new handle in RAM MOVE.L A0,D6 ; Save the copy in D6 MOVE.L D7,A0 ; Get the resource handle in A0 _DisposHandle ; Free the ROM handle MOVE.L D6,A0 ; Move the copy into A0 TST.L D6 ; Did we get the copy? BEQ.S @ReadFail ; -> No. Clear A0 and exit @SetTheSeed CMPI.W #1023,18(SP) ; Is this a request for a system clut? BLS.S @done ; -> Yes, leave the seed alone! CLR.L -(SP) ; make room for function return _rGetCTSeed ; get the next seed and leave it on the stack MOVE.L D6,A0 ; Get the copy in A0 MOVE.L (SP)+,([A0],ctSeed) ; Set the Seed in the copy _HNoPurge ; Make sure it doesn't go away BRA.S @Done ; -> Got It! @readFail SUBA.L A0,A0 ; A miserable failure. @done MOVEM.L (SP)+,D6/D7 ; Restore the work registers RTD #2 ; Lose ID, and go home.