; ; File: puLowLevel.a ; ; Contains: Low level assembly routines for the main part of the picture utilities package. This should ; not contain any code that only pertains to any of the built-in color finding methods (currently ; Popular and Median). It does contain code to work with the built-in data types like the exact ; color bank and the 555 histogram color bank. ; ; Written by: Dave Good. Some ideas stolen from Konstantin Othmer and Bruce Leak. Prototyped by Cris Rys. ; ; Copyright: © 1990 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; <15> 2/21/91 SMC KON,#83576: Black/white not getting suppressed on 32-bit ; pixmaps. ; <14> 2/4/91 SMC KON:BRC#82190, Fixed the way pixels in 2-bit pixmaps were ; getting recorded. ; <13> 1/16/91 JDR (dba) Renamed METHOD_SYSTEM to systemMethod, METHOD_POPULAR to ; popularMethod, and METHOD_MEDIAN to medianMethod ; <12> 12/7/90 SMC Fixed problem in spooling where the loop that copied data out of ; the buffer didn't check for a count of zero, so it copied 16 ; million bytes. With DDG. ; <11> 9/25/90 DDG Fixed a bug in RecordRGBColor where custom pick procs were ; getting passed a pointer to the blue component of the color, ; instead of the start of the color. Also changed the ; maximumCustomColors equate to be 5400. This ensures that the ; offset generated for this color will be less than $8000 (that ; is, it will be always be positive). This ensures that a ; limitation in the ÒenterCustomÓ routine is not exceded. Fixed a ; bug in the ÒReadPictGutsÓ that was passing a long for the buffer ; length to the userÕs spooling routine - it now passes a word. ; <10> 9/21/90 DDG Made changes from code review. ; <9> 8/16/90 DDG Added FillMemoryBytes from the puLowLevel.c file. ; <8> 8/5/90 DDG Fixed the code that returned an error when it couldnÕt load a ; custom pick proc. Changed RecordColors to take an extra ; parameter. Made the 32-bit routine in RecordPixels ÒspreadÓ the ; color bits out evenly over the word. Fixed the way I check for a ; custom color bank. Added code to GetPictByte to check for the ; current picture offset being outside the picture handle and made ; it return an error if this is the case. Updated the main ; internal information structure to match the new one in ; puPrivate.h. ; <7> 8/3/90 DDG Cleaned up the code a little. ; <6> 8/2/90 DDG Made 16-bit and 32-bit support custom procs. ; <5> 8/1/90 DDG Made the 16 and 32 bit loops support the suppressBlackAndWhite ; verb. ; <4> 7/30/90 DDG Changed the RecordPixels routine to allow AddExactColor to ; update the color count and the total number of unique colors. ; <3> 7/30/90 DDG All sorts of changes to support the generic colorPickMethod ; model and to support exact color banks. ; <2> 7/29/90 DDG Fixed header. ; <1> 7/29/90 DDG First checked in using new structure. ; ; To Do: ; MACHINE MC68000 CASE OBJ INCLUDE 'SysErr.a' INCLUDE 'Traps.a' INCLUDE 'PictUtil.a' ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;---------------------------------------------------------------------------------------------------- Buffer RECORD 0 ptr ds.l 1 size ds.l 1 ref ds.l 1 ENDR ;---------------------------------------------------------------------------------------------------- InternalInfoRecord RECORD 0 ext ds PictInfoRecord thePictInfoID ds.l 1 verb ds.w 1 colorPickMethod ds.w 1 colorsRequested ds.w 1 spoolBuffer ds Buffer lineBuffer ds Buffer bandBuffer ds Buffer spoolPtr ds.l 1 spoolMax ds.l 1 spoolPictHandle ds.l 1 spoolPictOffset ds.l 1 spoolPictLength ds.l 1 spoolPictError ds.w 1 spoolProc ds.l 1 colorBankType ds.w 1 colorBankBuffer ds Buffer colorBankIndex ds.w 1 colorTableBuffer ds Buffer colorTableEntries ds.w 1 indexCachePtr ds.l 1 foreColor ds.w 3 backColor ds.w 3 colorizing ds.w 1 pickProcData ds.l 1 ENDR ;---------------------------------------------------------------------------------------------------- ctTable EQU 8 maximumExactColors EQU 256 maximumCustomColors EQU 5400 ;¥¥¥ WARNING: this value times six treated as a word MUST be positive ¥¥¥ ;---------------------------------------------------------------------------------------------------- ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ IMPORT MedianDispatch IMPORT PopularDispatch ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ This function unpacks words of data, packed in the same way the PackBytes packs bytes. ;¥¥ ;¥¥ ;¥¥ C-STYLE ROUTINE ;¥¥ ;¥¥ void UnpackWords(unsigned char *sourcePtr, unsigned char **destPtrAddr, unsigned short destBytes); ;¥¥ ;¥¥ IN: SP-> long returnAddr ;¥¥ 4 long sourcePtr ;pointer to the source data. ;¥¥ 8 long destPtrAddr ;address of the destination pointer (we will update it). ;¥¥ 12 long destBytes ;only the low word of this parameter is significant. ;¥¥ ;¥¥ OUT: SP-> long returnAddr ;¥¥ 4 long sourcePtr ;C will clean these up for us. ;¥¥ 8 long destPtrAddr ;¥¥ 12 long destBytes ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ UnpackWords PROC EXPORT move.l 4(SP),A0 ;get source pointer into A0 move.l 8(SP),A1 ;get the address of dest pointer move.l (A1),A1 ;..and deref it into A1 move.l 12(SP),D2 ;get destination bytes into D2 ext.l D2 ;..and make it a long add.l A1,D2 ;D2 is now the limit pointer bra.s mainLoop copy ext.w D1 ;clear the high byte of the copy count @loop move.b (a0)+,(a1)+ ;copy a word of data without caring about alignment move.b (a0)+,(a1)+ dbra D1,@loop ;copy D1 words of data mainLoop cmp.l D2,A1 ;is dstPtr >= limit ? bhs.s exit ;yes, so we are thru move.b (A0)+,D1 ;get the opCode bpl.s copy ;0..127 -> copy 1..128 bytes of data neg.b D1 ;-1..-127 -> fill 2..128 bytes of data bvs.s mainLoop ;ignore $80 for backward compatibility fill ext.w D1 ;clear the high byte of the fill count move.b (A0)+,D0 ;get a word of data to fill with lsl.w #8,D0 or.b (A0)+,D0 @loop move.w D0,(A1)+ ;fill D1 words of data with the fill word dbra D1,@loop bra.s mainLoop ;go back to the main loop to get the next opCode exit move.l 8(SP),A0 ;get the address of dstPtr move.l A1,(A0) ;return the updated dstPtr rts ;C will clean up the stack ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ This function copies an array of 24-bit packed pixels into 32-bit direct pixels by clearing the ;¥¥ alpha channel for each destination pixel and then copying the components from the packed pixel. ;¥¥ ;¥¥ ;¥¥ C-STYLE ROUTINE ;¥¥ ;¥¥ void AddPadBytes(unsigned char *sourcePtr, unsigned char *destPtr, unsigned long pixelCount); ;¥¥ ;¥¥ IN: SP-> long returnAddr ;¥¥ 4 long sourcePtr ;pointer to the source data. ;¥¥ 8 long destPtr ;pointer to the destination area. ;¥¥ 12 long pixelCount ;number of pixels to add alpha channels to. ;¥¥ ;¥¥ OUT: SP-> long returnAddr ;¥¥ 4 long sourcePtr ;C will clean these up for us. ;¥¥ 8 long destPtr ;¥¥ 12 long pixelCount ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ AddPadBytes PROC EXPORT move.l 4(SP),A0 ;get the source pointer into A0 move.l 8(SP),A1 ;get the destination pointer into A1 move.l 12(SP),D0 ;get number of pixels into D0 @loop clr.b (A1)+ ;clear the alpha channel move.b (A0)+,(A1)+ ;copy red move.b (A0)+,(A1)+ ;copy green move.b (A0)+,(A1)+ ;copy blue subq.l #1,D0 ;loop thru all the pixels bhi.s @loop rts ;C will clean up the stack ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ This function takes a packed array of RGB in the form: RRRR...GGGG...BBBB... and it outputs ;¥¥ pixels in the form _RGB_RGB_RGB_RGB... where the Ò_Ó character represents the alpha channel, ;¥¥ which this routine clears to zero. ;¥¥ ;¥¥ ;¥¥ C-STYLE ROUTINE ;¥¥ ;¥¥ void MergeRGBData(char *srcPtr, char *dstPtr, unsigned long pixelCount); ;¥¥ ;¥¥ IN: SP-> long returnAddr ;¥¥ 4 long srcPtr ;pointer to the buffer that holds the input data described above. ;¥¥ 8 long dstPtr ;pointer to the output buffer where the result will be stored ;¥¥ 12 long pixelCount ;number of pixels of information to process ;¥¥ ;¥¥ OUT: SP-> long returnAddr ;¥¥ 4 long srcPtr ;C will clean these up for us. ;¥¥ 8 long dstPtr ;¥¥ 12 long pixelCount ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ MergeRGBData PROC EXPORT move.l A2,-(SP) ;save A2 on the stack move.l 4+4(SP),A0 ;get the source pointer into A0 move.l 8+4(SP),A1 ;get the destination pointer into A1 move.l 12+4(SP),D1 ;get number of pixels into D1 beq.s exit subq.w #1,D1 ;adjust the count so that we can use dbra (this is a word) moveq #3,D2 ;assume a component count of four (must decrement for dbra) tst.w 16+4(SP) ;do we have four components ? bne.s mainLoop ;yes, then jump to our main loop three move.l D1,D0 ;copy the pixel count into D1 move.l A1,A2 ;copy the destination pointer into A2 @alphaLoop clr.b (A2) ;clear all the alpha channel information, since we only have addq.l #4,A2 ;..three components dbra D0,@alphaLoop addq.l #1,A1 ;offset the main dstPtr to skip over the alpha channel moveq #2,D2 ;the component count is three (must decrement for dbra) mainLoop move.w D1,D0 ;copy the pixel count into D1 move.l A1,A2 ;copy the destination pointer into A2 for the pixel loop @pixelLoop move.b (A0)+,(A2) ;copy the actual pixel - note source always increments by one addq.l #4,A2 ;move to the next pixel (skip over the other components) dbra D0,@pixelLoop addq.l #1,A1 ;offset the main dstPtr to move to the next component dbra D2,mainLoop exit move.l (SP)+,A2 ;restore A2 rts ;C will clean up the stack ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ This function clears the specified number of bytes starting at the buffer pointer ;¥¥ ;¥¥ ;¥¥ C-STYLE ROUTINE ;¥¥ ;¥¥ void ClearMemoryBytes(char *bufferPtr, unsigned long length); ;¥¥ ;¥¥ IN: SP-> long returnAddr ;¥¥ 4 long bufferPtr ;pointer to the buffer to clear. ;¥¥ 8 long length ;number of bytes to clear. ;¥¥ ;¥¥ OUT: SP-> long returnAddr ;¥¥ 4 long bufferPtr ;C will clean these up for us. ;¥¥ 8 long length ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ClearMemoryBytes PROC EXPORT move.l 4(SP),A0 ;get the buffer pointer into A0 move.l 8(SP),D1 ;get the length into D1 beq.s exit clr.b D0 ;clear D0 for speed in the loop @loop move.b D0,(A0)+ subq.l #1,D1 bne.s @loop exit rts ;C will clean up the stack ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ This function dereferences the PictInfoID passed in and checks it for validity. ;¥¥ ;¥¥ ;¥¥ C-STYLE ROUTINE ;¥¥ ;¥¥ InternalPictInfoPtr DerefPictInfoID(PictInfoID theInfoHand, char *statePtr); ;¥¥ ;¥¥ IN: SP-> long returnAddr ;¥¥ 4 long theInfoHand ;handle to the PictInfo. ;¥¥ 8 long statePtr ;pointer to the state of theInfoHand ;¥¥ ;¥¥ OUT: SP-> long returnAddr ;¥¥ 4 long theInfoHand ;C will clean these up for us. ;¥¥ 8 long statePtr ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ DerefPictInfoID PROC EXPORT ;---------------------------------------------------------------------------------------------------- ; ¥¥¥ WARNING: theInfoHand must be A0, so we can pass it to memory manager calls. ¥¥¥ theInfoHand EQU A0 theInfoPtr EQU A1 temp EQU D0 ;---------------------------------------------------------------------------------------------------- WITH InternalInfoRecord ;---------------------------------------------------------------------------------------------------- move.l 4(SP),theInfoHand ;copy the info handle into a register move.l theInfoHand,temp ;check to see if it is NIL. If it is, then beq.s @error ;..return an error move.l (theInfoHand),theInfoPtr ;dereference the handle and check to see if the move.l theInfoPtr,temp ;..pointer is NIL. If it is, then return an error beq.s @error tst.w ext.version(theInfoPtr) ;check the version of the record to make sure that bne.s @error ;..it is valid. cmp.l thePictInfoID(theInfoPtr),theInfoHand ;check the Òinternal tagÓ of the record to bne.s @error ;..make sure that it is valid. _HGetState ;get the state for ÒtheInfoHandÓ move.l 8(SP),theInfoPtr ;re-use ÒtheInfoPtrÓ as a pointer to where to store move.b D0,(theInfoPtr) ;..the info handleÕs state. _MoveHHi ;move ÒtheInfoHandÓ high _HLock ;..and lock it down. move.l (theInfoHand),D0 ;return the dereferenced info handle. rts ;C will clean up the stack @error clr.l D0 rts ;---------------------------------------------------------------------------------------------------- ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ These functions read (or skip) data from the picture specified in the PictInfo record. All of ;¥¥ these routines call ReadPictGuts to do all the real work of reading (or skipping) the data. ;¥¥ Note that if an error occurs during the read process, the core routine will place an error code ;¥¥ in a field of the passed PictInfo record and it will then abort and return to the user. All ;¥¥ routines that call these routines must check this field for ;¥¥ ;¥¥ ;¥¥ GetPictByte - Reads one byte from the picture and returns it in D0 (standard C return). ;¥¥ GetPictWord - Reads one word from the picture and returns it in D0 (standard C return). ;¥¥ GetPictLong - Reads one long from the picture and returns it in D0 (standard C return). ;¥¥ GetPictData - Reads the specified number of bytes from the picture into the specified buffer. ;¥¥ ;¥¥ SkipPictByte - Skips over one byte in the picture. ;¥¥ SkipPictWord - Skips over one word in the picture. ;¥¥ SkipPictLong - Skips over one long in the picture. ;¥¥ SkipPictData - Skips over the specified number of bytes in the picture. ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ReadPictProcs PROC ;---------------------------------------------------------------------------------------------------- ;-- ;-- This function reads data from the picture specified by a field of the PictInfo record passed to ;-- it. Note that it calls ReadPictGuts to do all the work. ;-- ;-- ;-- C-STYLE ROUTINE ;-- ;-- void GetPictData(PictInfo *theInfoPtr, unsigned char *bufferPtr, long length); ;-- ;-- IN: SP-> long returnAddr ;-- 4 long theInfoPtr ;pointer to the PictInfo record. ;-- 8 long bufferPtr ;pointer to the buffer to store the returned data ;-- 12 long length ;number of bytes to read ;-- ;-- OUT: SP-> long returnAddr ;-- 4 long theInfoPtr ;C will clean this up for us. ;-- 8 long bufferPtr ;-- 12 long length ;-- ;---------------------------------------------------------------------------------------------------- EXPORT GetPictData GetPictData move.l 4(SP),A1 ;get the info pointer into A1 move.l 8(SP),D2 ;get the buffer pointer into D2 move.l 12(SP),D0 ;get the length into D0 bra.s ReadPictGuts ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- ;-- ;-- These functions get one (byte/word/long) from the picture specified by a field of the PictInfo ;-- record passed to these routines. Note that they call ReadPictGuts to do all the work. ;-- ;-- ;-- C-STYLE ROUTINE ;-- ;-- unsigned char GetPictByte(PictInfo *theInfoPtr); ;-- unsigned short GetPictWord(PictInfo *theInfoPtr); ;-- unsigned long GetPictLong(PictInfo *theInfoPtr); ;-- ;-- IN: SP-> long returnAddr ;-- 4 long theInfoPtr ;pointer to the PictInfo record. ;-- ;-- OUT: SP-> long returnAddr ;-- 4 long theInfoPtr ;C will clean this up for us. ;-- ;-- D0 is the returned result , the (byte/word/long) read from the picture) ;-- ;---------------------------------------------------------------------------------------------------- EXPORT GetPictLong EXPORT GetPictWord EXPORT GetPictByte GetPictLong moveq #4,D0 ;read four bytes out of the picture bra.s callGetGuts GetPictWord moveq #2,D0 ;read two bytes out of the picture bra.s callGetGuts GetPictByte moveq #1,D0 ;read one byte out of the picture callGetGuts move.l SP,D2 ;point our destination pointer at the correct spot, so that sub.l D0,D2 ;..we can simply move a long out of the stack. move.l 4(SP),A1 ;get the info pointer (our only parameter) into A1 clr.l -(SP) ;allocate four clear bytes on the stack bsr.s ReadPictGuts move.l (SP)+,D0 ;return the result rts ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- ;-- ;-- These functions skip one (byte/word/long) from the picture specified by a field of the PictInfo ;-- record passed to these routines. SkipPictData will skip the passed number of bytes from the picture. ;-- Note that they call ReadPictGuts to do all the work. ;-- ;-- ¥¥¥ WARNING ¥¥¥ Do not move this block of code away from ReadPictGuts !!! It relies on falling ;-- directly into ReadPictGuts !!! ¥¥¥ WARNING ¥¥¥ ;-- ;-- ;-- C-STYLE ROUTINE ;-- ;-- void SkipPictData(PictInfo *theInfoPtr, length); ;-- void SkipPictByte(PictInfo *theInfoPtr); ;-- void SkipPictWord(PictInfo *theInfoPtr); ;-- void SkipPictLong(PictInfo *theInfoPtr); ;-- ;-- IN: SP-> long returnAddr ;-- 4 long theInfoPtr ;pointer to the PictInfo record. ;-- 8 long length ;number of bytes to skip (*** this is only there for SkipPictData ***) ;-- ;-- OUT: SP-> long returnAddr ;-- 4 long theInfoPtr ;C will clean this up for us. ;-- 8 long length ;(*** this is only there for SkipPictData ***) ;-- ;---------------------------------------------------------------------------------------------------- EXPORT SkipPictData EXPORT SkipPictLong EXPORT SkipPictWord EXPORT SkipPictByte SkipPictData move.l 8(SP),D0 ;get the length to skip into D0 bra.s callSkipGuts SkipPictLong moveq #4,D0 ;skip four bytes from the picture bra.s callSkipGuts SkipPictWord moveq #2,D0 ;skip two bytes from the picture bra.s callSkipGuts SkipPictByte moveq #1,D0 ;skip one byte from the picture callSkipGuts move.l 4(SP),A1 ;get the info pointer (our only parameter) into A1 moveq #0,D2 ; bra.s ReadPictGuts ;NOTE: this will fall into ReadPictGuts ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- ;-- ;-- This function reads (or skips) multiple bytes out of a picture into a destination buffer. It is ;-- the only routine that needs to know how the picture spooling structures work. Since it is an ;-- internal function, its takes its parameters in registers. ;-- ;-- ;-- ASSEMBLY-STYLE ROUTINE ;-- ;-- IN: A1 (long) theInfoPtr ;pointer to the PictInfo record ;-- D2 (long) bufferPtr ;pointer to the buffer to store the data (if 0 then skip over the data) ;-- D0 (long) bufferLength ;number of bytes to read from the picture ;-- ;-- OUT: A0/D0-D2 are trashed ;-- ;-- ;---------------------------------------------------------------------------------------------------- WITH InternalInfoRecord ReadPictGuts tst.l D0 beq.s exit move.l spoolPtr(A1),D1 ;check to see if we are spooling (spoolPtr != 0) beq.s noSpooling move.l D1,A0 ;set A0 to the current spool buffer pointer add.l D0,D1 ;find out if reading these bytes will overflow the buffer. cmp.l spoolMax(A1),D1 ;if so, then copy the remaining bytes and re-load the buffer bhi.s mustLoad copyData add.l D0,spoolPtr(A1) ;move the spoolPtr forward by the number of bytes read copyFromHandle add.l D0,spoolPictOffset(A1) ;add the number of bytes read to the picture offset tst.l D2 ;are we skipping the picture data ? If so, then exit beq.s exit move.l A1,-(SP) ;save our pictInfo pointer move.l D2,A1 ;copy the buffer pointer to an address register @loop move.b (A0)+,(A1)+ ;copy the data to the buffer subq.l #1,D0 bne.s @loop move.l A1,D2 ;copy our scratch address register back to the buffer pointer move.l (SP)+,A1 ;restore our pictInfo pointer exit rts ;---------------------------------------------------------------------------------------------------- mustLoad sub.l spoolMax(A1),D1 ;D1 is now the number of bytes past the buffer that we need sub.l D1,D0 ;D0 is now the remaining number of bytes in the buffer beq.s skipcopy ;buffer was completely empty so there's nothing to copy out first bsr.s copyData ;copy the remaining bytes out of the buffer skipcopy move.l D1,D0 ;set D0 to be the number of bytes that we still need to move movem.l D0/D2/A1,-(SP) ;save our parameters move.l spoolBuffer.ptr(A1),D0 ;get the pointer to the start of our buffer into D0 move.l D0,spoolPtr(A1) ;..and copy it to the current buffer pointer move.l D0,-(sp) ;push the buffer pointer for the spooling routine move.w spoolBuffer.size+2(A1),-(sp) ;push the buffer size for the spooling routine (as a word) move.l spoolProc(A1),A0 ;get the address of the spooling routine jsr (A0) ;..and call it movem.l (SP)+,D0/D2/A1 ;restore our parameters bra.s ReadPictGuts ;re-enter the routine to get the remaining data ;---------------------------------------------------------------------------------------------------- noSpooling move.l spoolPictHandle(A1),A0 ;dereference the picture handle into A0 move.l (A0),A0 move.l spoolPictOffset(A1),D1 ;get the current picture offset in D1 and make A0 a pointer add.l D1,A0 ;..to the current spot. add.l D0,D1 ;then, add the number of bytes we are reading to the current cmp.l spoolPictLength(A1),D1 ;..offset, and check to see if we have overflowed... bls.s copyFromHandle ;if we havenÕt, then copy the data out of the picture handle error move.w #pictureDataErr,spoolPictError(A1) clr.l D0 rts ENDWITH ;---------------------------------------------------------------------------------------------------- ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ This records color information for the pixels in the passed pixMap. ;¥¥ NOTE: Since this is a C-Style routine, all the parameters are passed as four byte quanities. ;¥¥ ;¥¥ ;¥¥ C-STYLE ROUTINE ;¥¥ ;¥¥ void RecordPixels( PictInfo *theInfoPtr, short width, short height, short rowBytes, ;¥¥ unsigned char *baseAddr, short thePixelSize ); ;¥¥ ;¥¥ IN: SP-> long returnAddr ;¥¥ 4 long theInfoPtr ;pointer to the PictInfo record ;¥¥ 8 long width ;width of the pixMap in pixels ;¥¥ 12 long height ;height of the pixMap in pixels ;¥¥ 16 long rowBytes ;the number of bytes in each row of pixels ;¥¥ 20 long baseAddr ;pointer to the base of the pixel data ;¥¥ 24 long thePixelSize ;number of bits in each pixel ;¥¥ ;¥¥ OUT: SP-> long returnAddr ;¥¥ 4 long theInfoPtr ;C will clean these up for us. ;¥¥ 8 long width ;¥¥ 12 long height ;¥¥ 16 long rowBytes ;¥¥ 20 long baseAddr ;¥¥ 24 long thePixelSize ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ RecordPixels PROC EXPORT ;---------------------------------------------------------------------------------------------------- IMPORT RECORDCOLORS IMPORT ConvertBankTo555 IMPORT MakeIndexCache ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- ; ¥¥¥ WARNING: DonÕt casually reorder these registers !! There are several dependancies on some of ; these variables corresponding to particular registers in the rest of this code. Look thru all the ; comments in the entire RecordPixels routine before changing these ! ¥¥¥ lineStart EQU A0 rowBytes EQU A1 pixelPtr EQU A2 theInfoPtr EQU A3 colorBankStart EQU A4 theTable EQU A5 width EQU D0 height EQU D1 pixelCount EQU D2 tempWidth EQU D3 pixelValue EQU D4 index EQU D5 bankIndex EQU D6 depth EQU D7 ; aliases for various variables... suppressFlag EQU D7 ;this is the same as depth registersUsed REG D2-D7/A2-A5 regSize EQU 40 ;---------------------------------------------------------------------------------------------------- WITH InternalInfoRecord ;---------------------------------------------------------------------------------------------------- movem.l registersUsed,-(SP) ;save our work registers move.l regSize+4(SP),theInfoPtr ;copy our passed parameters into registers move.l regSize+8(SP),width beq exit move.l regSize+12(SP),height move.l regSize+16(SP),rowBytes move.l regSize+20(SP),lineStart move.l regSize+24(SP),depth move.l indexCachePtr(theInfoPtr),theTable ;copy some fields that we are going to need move.l colorBankBuffer.ptr(theInfoPtr),colorBankStart ;..into registers cmp.w #8,depth ;dispatch to the correct loop beq.s enter8Bit cmp.w #4,depth beq.s enter4Bit cmp.w #16,depth beq enter16Bit cmp.w #32,depth beq enter32Bit cmp.w #2,depth beq enter2Bit ;---------------------------------------------------------------------------------------------------- ; This section is the pixel loop for 1-Bit pixels. It looks at the pixels one byte at a time, ; shifting the highest bit into ÒindexÓ and then calling ÒRecordIndexÓ to record the pixel. Note ; that ÒindexÓ is a word. Also note that ÒRecordIndexÓ will trash ÒindexÓ and ÒbankIndexÓ. ; loop1Bit move.l lineStart,pixelPtr ;make pixelPtr a pointer to the current pixel move.w width,tempWidth ;tempWidth is our loop counter @byteLoop move.b (pixelPtr)+,pixelValue ;copy one bytes worth of pixels into pixelValue moveq #8,pixelCount ;there are eight 1-bit pixels in one byte cmp.w pixelCount,tempWidth ;do we have more than eight pixels left ? If so, then enter bge.s @enter ;..the loop move.w tempWidth,pixelCount ;nope, so only go thru the correct number of pixels @enter sub.w pixelCount,tempWidth ;decrement our main loop counter by the correct amount @loop add.b pixelValue,pixelValue ;(4) shift one bit (pixel) off the left end of our byte clr.w index ;(4) clear the index, so we can roll in a bit later roxl.w #1,index ;(8) move it into index bsr.s RecordIndex ;record the index in the color bank dbra pixelCount,@loop ;loop thru all the pixels in the byte tst.w tempWidth ;are there more bytes left ? If so, then loop back bne.s @byteLoop add.l rowBytes,lineStart ;move lineStart to the next line and loop through enter1Bit dbra height,loop1Bit ;..all the rows in the pixMap bra.s exit ;---------------------------------------------------------------------------------------------------- ; This section is the pixel loop for 8-Bit pixels. Note that since ÒindexÓ is a word, and calling ; ÒRecordIndexÓ trashes ÒindexÓ and ÒbankIndexÓ, we must clear the high byte of index every time ; thru the loop ; loop8Bit move.l lineStart,pixelPtr ;make pixelPtr a pointer to the current pixel move.w width,tempWidth ;tempWidth is our loop counter bra.s @enter @loop clr.w index ;clear the upper byte of index move.b (pixelPtr)+,index ;copy the pixel into index bsr.s RecordIndex ;record this pixel into the color bank @enter dbra tempWidth,@loop ;loop through all the pixels in the line add.l rowBytes,lineStart ;move lineStart to the next line and loop through enter8Bit dbra height,loop8Bit ;..all the rows in the pixMap bra.s exit ;---------------------------------------------------------------------------------------------------- ; This section is the pixel loop for 4-Bit pixels. We copy the byte that contains the current pixel ; into ÒpixelValueÓ and then put the nibbles into ÒindexÓ to record each one. We look thru the nibbles ; in ascending pixel order, since we donÕt know whether ÒwidthÓ pixels will end in the middle of a ; byte or not. ¥¥¥ WARNING: DonÕt move this code without fixing the end that falls thru into exit ¥¥¥ ; loop4Bit move.l lineStart,pixelPtr ;make pixelPtr a pointer to the byte that contains move.w width,tempWidth ;..the current pixel. tempWidth is going to be our bra.s @enter ;..loop counter. @loop clr.w index ;clear the upper byte of the index move.b (pixelPtr)+,pixelValue ;copy the current two pixels into pixelValue move.b pixelValue,index ;copy these pixels into the low byte of index lsr.w #4,index ;shift to get only the first pixel into index bsr.s RecordIndex ;record the pixel into the color bank dbra tempWidth,@nextPixel ;decrement our width and goto the next line if bra.s @nextLine ;..we have reached the end of the current one @nextPixel move.b pixelValue,index ;copy our two pixels in again and this time use and.w #$000F,index ;..the second pixel. bsr.s RecordIndex @enter dbra tempWidth,@loop @nextLine add.l rowBytes,lineStart ;move lineStart to the next line and loop through enter4Bit dbra height,loop4Bit ;..all the rows in the pixMap ; fall thru into exit ;---------------------------------------------------------------------------------------------------- exit movem.l (SP)+,registersUsed ;restore our work registers rts ;C will clean up the stack ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- ; This subroutine takes the true pixel value in ÒindexÓ, treated as a word. It records color info ; for the color associated with the passed ÒindexÓ. This routine trashes bankIndex, but it saves ; and restores any other registers that it uses. ; Note that there are two other entry points for this function: ; ; 1 - ÒRecordIndex2Ó this takes the ÒindexÓ pre-shifted into an index into a table of long offsets. ; ; 2 - ÒRecord16BitÓ this ignores ÒindexÓ completely and takes ÒbankIndexÓ (a long) as a parameter. ; NOTE: bankIndex must NOT be negative when calling ÒRecord16BitÓ, or else the ; special code will be called and this code will try to use ÒindexÓ. ; ; This subroutine assumes that it can look up an index into the color bank, where it can record ; the color, but in some cases that index will actually be a flag (indicated by a negative number) ; that tells ÒRecordIndexÓ to do something special with color. In these cases ÒSpecialEntryÓ is ; called (see below). ; RecordIndex lsl.w #2,index ;the index (pixel value) is to a table of longs RecordIndex2 move.l (theTable,index.w),bankIndex ;if the table index is minus, then this index is special bmi.s SpecialEntry Record16Bit tst.w (colorBankStart,bankIndex.l) ;if the color bank count is zero, then add a new color beq.s @newColor @1 add.w #1,(colorBankStart,bankIndex.l) ;increment the color count and check for overflow bmi.s @saturated rts @newColor addq.l #1,ext.uniqueColors(theInfoPtr) ;increment the uniqueColor count, then increment the color bra.s @1 ;..count. @saturated move.w #$7FFF,(colorBankStart,bankIndex.l) ;restore the saturated value and exit rts ;---------------------------------------------------------------------------------------------------- ; This code fragment is actually a part of the ÒRecordIndexÓ subroutine and in some cases it actually ; jumps back to the second entry for ÒRecordIndexÓ, (ÒRecordIndex2Ó). We are going to be calling ; C-Subroutines from this routine, so we have to save C scratch registers (D0-D2/A0-A1) AND we MUST ; make sure that none of our local register variables that we are using in this section are stored ; in any of these scratch registers. Currently these scratch registers correspond to the following ; variables: width, height, pixelCount, lineStart, and rowBytes. Since we donÕt use any of these ; variables in this subroutine, we can temporarily trash them; of course, we do restore them when ; we exit. ; SpecialEntry movem.l D0-D2/A0-A1,-(SP) ;save the registers that the C routines will trash addq.l #1,bankIndex ;compare bankIndex with -1 (add exact color) beq.s @addExactColor addq.l #1,bankIndex ;compare bankIndex with -2 (suppress color) beq.s @exit @recordCustom bsr enterCustom ;this sets up a destPtr (A1) to the color bank entry move.l colorTableBuffer.ptr(theInfoPtr),A0 ;get a pointer to the 48-Bit color for this index move.w index,bankIndex add.w bankIndex,bankIndex ;cheap multiply by two (this gives pixel value * 8) lea 2(A0,bankIndex.w),A0 ;skip over the value field in the ColorSpec move.l (A0)+,(A1)+ ;copy the red and green components of the 48-Bit color move.w (A0),(A1) ;copy the blue component of the color into the color bank @exit movem.l (SP)+,D0-D2/A0-A1 ;restore the registers that we saved and exit rts @addExactColor cmp.l #maximumExactColors,ext.uniqueColors(theInfoPtr) bge.s @convertExact ;if there are too many exact colors, then convert to histogram move.l colorTableBuffer.ptr(theInfoPtr),A0 ;get a pointer to the 48-Bit color for this index move.w index,bankIndex add.w bankIndex,bankIndex ;cheap multiply by two (this gives pixel value * 8) lea 2(A0,bankIndex.w),A0 ;skip over the value field in the ColorSpec move.l ext.uniqueColors(theInfoPtr),bankIndex lsl.w #3,bankIndex ;the number of exact colors ALWAYS fits in a word. lea (colorBankStart,bankIndex.w),A1 ;get a pointer to the next available color bank entry move.w #1,(A1)+ ;the count for the new entry is one. move.l (A0)+,(A1)+ ;copy the red and green components of the 48-Bit color move.w (A0),(A1) ;copy the blue component of the color into the color bank move.l bankIndex,(theTable,index.w) ;set the correct entry in the cache table to our offset addq.l #1,ext.uniqueColors(theInfoPtr) ;increment the number of unique colors, then restore bra.s @exit ;..the registers and exit @convertExact move.l theInfoPtr,-(SP) ;use this parameter for both routines (C doesnÕt remove jsr ConvertBankTo555 ;..the parameters from the stack and these routines jsr MakeIndexCache ;..donÕt modify them. addq.l #4,SP ;clean up the stack for both movem.l (SP)+,D0-D2/A0-A1 ;restore the registers that we saved and jump back to bra RecordIndex2 ;..the main pixel routine to actually record this pixel ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- ; This section is the pixel loop for 2-Bit pixels. It looks at the pixels one byte at a time, ; shifting the highest two bits into ÒindexÓ and then calling ÒRecordIndexÓ to record the pixel. ; Note that ÒindexÓ is a word. Also note that ÒRecordIndexÓ will trash ÒindexÓ and ÒbankIndexÓ. ; loop2Bit move.l lineStart,pixelPtr ;make pixelPtr a pointer to the current pixel move.w width,tempWidth ;tempWidth is our loop counter @byteLoop move.b (pixelPtr)+,pixelValue ;copy one bytes worth of pixels into pixelValue moveq #4,pixelCount ;there are four 2-bit pixels in one byte cmp.w pixelCount,tempWidth ;do we have more than four pixels left? bge.s @all4 ;yes, skip adjustment move.w tempWidth,pixelCount ;nope, so only go thru the correct number of pixels @all4 sub.w pixelCount,tempWidth ;decrement our main loop counter by the correct amount bra.s @enter ;<14> go enter the loop @loop clr.w index ;clear the index, so we can roll in a bit later add.b pixelValue,pixelValue ;shift one bit (half a pixel) off the left end of our byte addx.w index,index ;<14> move it into index add.b pixelValue,pixelValue ;shift the other half of the pixel into the index addx.w index,index ;<14> move it into index bsr RecordIndex ;record the index in the color bank @enter dbra pixelCount,@loop ;loop thru all the pixels in the byte tst.w tempWidth ;are there more bytes left ? If so, then loop back bne.s @byteLoop add.l rowBytes,lineStart ;move lineStart to the next line and loop through enter2Bit dbra height,loop2Bit ;..all the rows in the pixMap bra exit ;---------------------------------------------------------------------------------------------------- ; This section is the pixel loop for 16-Bit direct pixels. Since these pixels are direct, we donÕt ; have an index cache (index look up table). This means that we need to worry about both suppressing ; black and white directly in this loop and checking to see whether we are recording into a custom ; color bank. We use a local variable (ÒsuppressFlagÓ) to indicate whether or not we are suppressing ; black and white. ¥¥¥ WARNING: this local (ÒsuppressFlagÓ) is the same register as ÒdepthÓ!! We can ; do this since we donÕt use depth for anything after we have dispatched to the proper bit loop. ¥¥¥ ; ; The code flow is as follows: We grab each pixel and check to see if we are suppressing. If so, then ; we check to see if the pixel is black or white and if it is, we skip it. We then shift the pixel ; left one place (to make it an index into our histogram). After the shift, we check to see if we are ; recording into a custom color bank and if we arenÕt, then we copy the ÒindexÓ into ÒbankIndexÓ and ; call ÒRecord16BitÓ. This routine will use ÒbankIndexÓÊas an index into the histogram and increment ; the color count that it finds there. It will also check for saturation and update the unique color ; count. ; ; If we are recording into a custom bank, then we grab the correct five bits for each component (in ; red, green, blue order) and call a subroutine to smear each of them out into a word and store this ; color component word in the color bank cache. Before doing this, we call ÒenterCustomÓ, which will ; check to see if the color bank cache is full, flush it out if it is, and then setup A1 as a pointer ; to the next available spot in the color bank cache. ; enter16Bit move.w verb(theInfoPtr),suppressFlag ;set the flag to indicate whether or not we are suppressing and.w #suppressBlackAndWhite,suppressFlag ;..black and white bra.s @enterMain @loop16Bit move.l lineStart,pixelPtr ;start pixelPtr at the beginning of the current line move.w width,tempWidth ;tempWidth is our loop counter bra.s @nextPixel ;enter the loop, so that we can use a dbra @loop move.w (pixelPtr)+,index ;get the pixel tst.w suppressFlag ;are we supressing black and white ? If not, then skip beq.s @noSuppress ;..the black/white check. and.w #$7FFF,index ;mask out the alpha channel information. beq.s @nextPixel ;was the index black ? If so, then skip it. cmp.w #$7FFF,index ;was the index white ? If so, then skip it. beq.s @nextPixel @noSuppress add.w index,index ;cheap multiply by two to get histogram index tst.w colorBankType(theInfoPtr) ;are we recording for a custom pick proc ? If so, then do bmi.s @recordCustom ;..some special stuff. clr.l bankindex ;clear the high word of bank index move.w index,bankIndex bsr Record16Bit ;otherwise, record the color using this histogram index @nextPixel dbra tempWidth,@loop ;move to the next pixel and loop add.l rowBytes,lineStart ;move to the next line and loop @enterMain dbra height,@loop16Bit bra exit ;exit the main routine here. @recordCustom movem.l D0-D2/A0-A1,-(SP) ;save the registers that Pascal is going to trash bsr.s enterCustom ;this sets up a destPtr (A1) to the color bank entry move.w #10,D1 ;use D1 to hold the shift count of ten move.w index,pixelValue ;copy the pre-shifted (by two) pixel value into pixelValue bsr.s @smearAndCopy ;smear out the red component and copy it to the color bank move.w index,pixelValue ;get the pre-shifted (by two) pixel value again lsl.w #5,pixelValue ;shift to get the green component bsr.s @smearAndCopy ;smear it out to fill a word and copy it to the color bank move.w index,pixelValue ;get the pre-shifted (by two) pixel value for the last time lsl.w D1,pixelValue ;shift to get the blue component bsr.s @smearAndCopy ;smear it out to fill a word and copy it to the color bank movem.l (SP)+,D0-D2/A0-A1 ;restore the registers that we saved and jump to the next bra.s @nextPixel ;..pixel @smearAndCopy and.w #$F800,pixelValue ;smear the pixel component into a 16-bit color word, the move.w pixelValue,D0 ;..same way that quickdraw does. Note that the component lsr.w #5,D0 ;..comes in the upper 5 bits of the pixelValue word or.w D0,pixelValue move.w pixelValue,D0 lsr.w D1,D0 or.w D0,pixelValue move.w pixelValue,(A1)+ ;move the pixel component into the color bank. rts ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- ; To record colors into a custom color bank, we cache them as 48-Bit colors in our own internal color ; bank until our bank is almost full. At this point, we call the custom pick proc to record all the ; cached colors and then we set the ÒcolorBankIndexÓ back to zero (flushing the cache). ; ; This subroutine handles all the checking to see if our cache is full as well as maintaining the cache ; index (ÒcolorBankIndexÓ) and calculating a pointer to the next available spot in the custom color ; bank cache. This routine should be called every time a color needs to be recorded into a custom color ; bank. It takes no real parameters, though it does use ÒtheInfoPtrÓ and ÒcolorBankStartÓ and it returns ; the pointer to the next available spot in A1. ; ; ASSEMBLY-STYLE ROUTINE ; ; IN: Rx (long) theInfoPtr ;pointer to the PictInfo record (in a register) ; Rx (long) colorBankStart ;pointer to the start of the color bank cache ; ; OUT: ÒbankIndexÓ and D0 are trashed. ; A1 (long) cachePtr ;pointer to the next available spot in the color bank cache. ; enterCustom clr.l bankIndex ;clear the high word of bank index move.w colorBankIndex(theInfoPtr),bankIndex cmp.w #maximumCustomColors,bankIndex ;check to see if we are overflowing the color bank. bge.s @recordColors ;we are, so pass the colors to the user proc. add.w bankIndex,bankIndex ;multiply by six the get a byte index into the color move.w bankIndex,D0 ;..bank add.w bankIndex,bankIndex add.w D0,bankIndex lea (colorBankStart,bankIndex.w),A1 ;get our destination pointer in the color bank add.w #1,colorBankIndex(theInfoPtr) ;increment the color bank index rts @recordColors subq.l #2,SP ;space for the OSErr result move.l pickProcData(theInfoPtr),-(SP) ;pass the user procÕs data handle move.l colorBankStart,-(SP) ;pass a pointer to the start of the saved colors move.l bankIndex,-(SP) ;pass the number of colors to record (high word is zero) pea ext.uniqueColors(theInfoPtr) ;pass a pointer to the number of unique colors move.w colorPickMethod(theInfoPtr),-(SP) ;pass the method ID, so we know which method to call jsr RECORDCOLORS ;call the user proc. addq.l #2,SP ;ignore the OSErr result clr.w colorBankIndex(theInfoPtr) ;clear the color bank index and exit bra.s enterCustom ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- ; This section is the pixel loop for 32-Bit direct pixels. Since these pixels are direct, we donÕt ; have an index cache (index look up table). This means that we need to worry about both suppressing ; black and white directly in this loop and checking to see whether we are recording into a custom ; color bank. We use a local variable (ÒsuppressFlagÓ) to indicate whether or not we are suppressing ; black and white. ¥¥¥ WARNING: this local (ÒsuppressFlagÓ) is the same register as ÒdepthÓ!! We can ; do this since we donÕt use depth for anything after we have dispatched to the proper bit loop. ¥¥¥ ; ; The code flow is as follows: We grab each pixel and check to see if we are suppressing. If so, then ; we check to see if the pixel is black or white and if it is, we skip it. We then check to see if we ; are recording into a custom color bank. If we arenÕt, then do some tricky stuff to convert the ; 32-bit direct color into a 16-bit direct color, shift the index left one bit to get an index into ; the histogram, and finally record the index by calling ÒRecord16BitÓ. ; ; If we are recording into a custom bank, we call ÒenterCustomÓ, which will check to see if the color ; bank cache is full, flush it out if it is, and then setup A1 as a pointer to the next available spot ; in the color bank cache. We then copy the pixel value into the color bank, smearing out the ; components as we go and then we move on to the next pixel. ; enter32Bit move.w verb(theInfoPtr),suppressFlag ;set the flag to indicate whether or not we are suppressing and.w #suppressBlackAndWhite,suppressFlag ;..black and white bra.s @enterMain @recordCustom tst.w suppressFlag ;are we suppressing black and white ? If no, then skip the beq.s @noSuppress ;..check for those colors and.l #$00FFFFFF,pixelValue ;mask out the alpha channel information for the color compare beq.s @nextPixel ;is the pixel black ? If so, then skip it cmp.l #$00FFFFFF,pixelValue ;is the pixel white ? If so, then skip it beq.s @nextPixel @noSuppress movem.l D0-D2/A0-A1,-(SP) ;save the registers that Pascal is going to trash bsr.s enterCustom ;this sets up a destPtr (A1) to the color bank entry move.b pixelValue,4(A1) ;copy the blue component twice (smear it out) into the move.b pixelValue,5(A1) ;..color bank. swap pixelValue ;get the red component into the lowest byte move.b pixelValue,(A1)+ ;copy the red component twice (smear it out) into the move.b pixelValue,(A1)+ ;..color bank and advance the color bank pointer swap pixelValue lsr.w #8,pixelValue ;get the green component into the lowest byte move.b pixelValue,(A1)+ ;copy the green component twice (smear it out) into the move.b pixelValue,(A1) ;..color bank. movem.l (SP)+,D0-D2/A0-A1 ;restore the registers that we saved and jump to the next bra.s @nextPixel ;..pixel @loop32Bit move.l lineStart,pixelPtr ;start pixelPtr at the beginning of the current line move.w width,tempWidth ;tempWidth is our loop counter bra.s @nextPixel ;enter the loop, so that we can use a dbra @loop move.l (pixelPtr)+,pixelValue ;get the entire pixel into pixelValue tst.w colorBankType(theInfoPtr) ;are we recording into a custom bank ? If so, then skip out bmi.s @recordCustom ;..to recordCustom ;xxxxxxxx.RRRRRrrr.GGGGGggg.BBBBBbbb <15> LSR.L #3,pixelValue ;000xxxxx.xxxRRRRR.rrrGGGGG.gggBBBBB <15> LSL.B #3,pixelValue ;000xxxxx.xxxRRRRR.rrrGGGGG.BBBBB000 <15> LSL.W #3,pixelValue ;000xxxxx.xxxRRRRR.GGGGGBBB.BB000000 <15> LSR.L #6,pixelValue ;00000000.0xxxxxxx.xRRRRRGG.GGGBBBBB <15> ADD.W pixelValue,pixelValue ;00000000.0xxxxxxx.RRRRRGGG.GGBBBBB0 <15> MOVEQ #0,bankIndex ;clear the high word of bankIndex <15> MOVE.W pixelValue,bankIndex ;put 16 bit index into bankIndex <15> beq.s @suppressTest ;go check suppress flag if color is white <15> cmp.w #$FFFE,bankIndex ;is index white? <15> bne.s @noSuppressTest ;no, go record the index <15> @suppressTest tst.w suppressFlag ;are we suppressing black and white? <15> bne.s @nextPixel ;yes, then ignore the black or white index <15> @noSuppressTest bsr Record16Bit ;record the index @nextPixel dbra tempWidth,@loop ;loop back to the next pixel add.l rowBytes,lineStart ;move on to the next line @enterMain dbra height,@loop32Bit bra exit ;---------------------------------------------------------------------------------------------------- ENDWITH ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ This function inserts a color into the color bank given a pointer to the 48-bit RGB value for the ;¥¥ color. ;¥¥ ;¥¥ ;¥¥ C-STYLE ROUTINE ;¥¥ ;¥¥ void RecordRGBColor(PictInfo *sourcePtr, RGBColor *theColorPtr); ;¥¥ ;¥¥ IN: SP-> long returnAddr ;¥¥ 4 long theInfoPtr ;pointer to the PictInfo record ;¥¥ 8 long theColorPtr ;pointer to the 48-bit color to record ;¥¥ ;¥¥ OUT: SP-> long returnAddr ;¥¥ 4 long theInfoPtr ;C will clean these up for us. ;¥¥ 8 long theColorPtr ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ RecordRGBColor PROC EXPORT ;---------------------------------------------------------------------------------------------------- IMPORT RECORDCOLORS IMPORT ConvertBankTo555 IMPORT MakeIndexCache ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- theInfoPtr EQU A0 theColorPtr EQU A1 colorBankPtr EQU A2 temp EQU D0 counter EQU D1 theRedAndGreen EQU D2 theBlue EQU D3 registersUsed REG A2/D3 regSize EQU 8 ;---------------------------------------------------------------------------------------------------- WITH InternalInfoRecord ;---------------------------------------------------------------------------------------------------- movem.l registersUsed,-(SP) ;save our work registers move.l regSize+4(SP),theInfoPtr ;copy our passed parameters into registers move.l regSize+8(SP),theColorPtr move.l (theColorPtr),theRedAndGreen ;copy the color components into registers move.w 4(theColorPtr),theBlue move.w verb(theInfoPtr),temp ;check to see whether or not we are suppressing black and and.w #suppressBlackAndWhite,temp ;..white beq.s useColor ;nope, so record the color we were passed tst.l theRedAndGreen ;check to see if the red and green components of the color bne.s @notBlack ;..are black. tst.w theBlue ;check to see if the blue component of the color is black. beq exit ;if so, then skip this color (exit the routine) @notBlack moveq #-1,temp ;set temp to be white cmp.l temp,theRedAndGreen ;check to see if the red and green components of the color bne.s useColor ;..are white. If not, then record this color. cmp.w temp,theBlue ;check to see if the blue component of the color is white. beq exit ;if so, then skip this color (exit the routine) useColor move.l colorBankBuffer.ptr(theInfoPtr),colorBankPtr ;setup a pointer to our color bank tst.w colorBankType(theInfoPtr) ;find out what type of color bank we are recording into beq.s doExact ;if zero, then the color bank is exact bpl.s doHistogram ;if positive, then itÕs a histogram ;---------------------------------------------------------------------------------------------------- doCustom subq.l #2,SP ;space for the OSErr result move.l pickProcData(theInfoPtr),-(SP) ;pass the user procÕs data handle move.l theColorPtr,-(SP) ;pass a pointer to the start of the saved colors move.l #1,-(SP) ;we are recording one color pea ext.uniqueColors(theInfoPtr) ;pass a pointer to the number of unique colors move.w colorPickMethod(theInfoPtr),-(SP) ;pass the method ID, so we know which method to call jsr RECORDCOLORS ;call the user proc. addq.l #2,SP ;ignore the OSErr result bra.s exit ;---------------------------------------------------------------------------------------------------- doExact move.l ext.uniqueColors(theInfoPtr),counter ;setup our loop counter (we only need the low word) bra.s @enter ;enter the loop at the bottom, so that we can use dbra @loop cmp.l 2(colorBankPtr),theRedAndGreen ;do the red and green components match ? If not, then move bne.s @noMatch ;..on to the next color. cmp.w 6(colorBankPtr),theBlue ;does the blue component match ? If so, then the color has beq.s colorExists ;..already been recorded, so jump to incrementing its count @noMatch addq.l #8,colorBankPtr ;move the color bank pointer to the next exact color. @enter dbra counter,@loop ;count down and loop back. @addExactColor cmp.l #maximumExactColors,ext.uniqueColors(theInfoPtr) bge.s @convertExact ;if there are too many exact colors, convert to histogram move.w #1,(colorBankPtr)+ ;the count for the new entry is one. move.l theRedAndGreen,(colorBankPtr)+ ;copy the red and green components of the 48-Bit color move.w theBlue,(colorBankPtr) ;copy the blue component of the color into the color bank addq.l #1,ext.uniqueColors(theInfoPtr) ;increment the number of unique colors, then restore bra.s exit ;..the registers and exit @convertExact movem.l D0-D2/A0-A1,-(SP) ;save registers that C is going to trash. move.l theInfoPtr,-(SP) ;use this parameter for both routines (C doesnÕt remove jsr ConvertBankTo555 ;..the parameters from the stack and these routines jsr MakeIndexCache ;..donÕt modify them. addq.l #4,SP ;clean up the stack for both. movem.l (SP)+,D0-D2/A0-A1 ;restore the registers that we saved and jump back to bra.s useColor ;..the main color dispatch to actually record this color. ;---------------------------------------------------------------------------------------------------- doHistogram move.l #10,temp ;setup a shift count register for getting the upper five lsr.w temp,theBlue ;..bits of blue multiplied by 2 (to be an index into and.w #$003E,theBlue ;..the histogram) add.w theBlue,colorBankPtr ;take the blue component into account in the color bank ptr lsr.w #5,theRedAndGreen ;work with just the green component (the low word) and get and.w #$07C0,theRedAndGreen ;the upper five bits multiplied by 2 and then by 32 to add.w theRedAndGreen,colorBankPtr ;..make an index into the histogram. swap theRedAndGreen ;now work with just the red component (the high word) and and.w #$F800,theRedAndGreen ;get the upper five bits multiplies by 2, then 32, then 32 add.w theRedAndGreen,colorBankPtr ;..again to be an index into the histogram. tst.w (colorBankPtr) ;is the color already in the histogram ? If so, then skip bne.s colorExists ;..to colorExists addq.l #1,ext.uniqueColors(theInfoPtr) ;otherwise, increment the number of unique colors ;---------------------------------------------------------------------------------------------------- colorExists addq.w #1,(colorBankPtr) ;increment the color occurrance count bpl.s exit ;if we did not overflow, then exit move.w #$7FFF,(colorBankPtr) ;restore the saturated value ; bra.s exit ;NOTE: this will fall into exit ;---------------------------------------------------------------------------------------------------- exit movem.l (SP)+,registersUsed ;restore our work registers rts ;C will clean up the stack ;---------------------------------------------------------------------------------------------------- ENDWITH ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ;¥¥ ;¥¥ These functions dispatch to the specified pick method proc. Theoretically, these could be ;¥¥ replaced by DispatchHelper, but that would require documentation changes, so we wonÕt do this ;¥¥ yet. ;¥¥ ;¥¥ The main routine here is LoadPickMethod, the routine that actually dispatches to the custom proc. ;¥¥ It takes the total bytes of parameters plus four (for the return address) in D1 and the selector ;¥¥ number in D0. Note that the total bytes of parameters includes two bytes for the return result (OSErr). ;¥¥ On entry, the stack looks like: ;¥¥ ;¥¥ IN: SP-> long mainRTS ;¥¥ 4 D1 - 6 parameters ;the parameters for the particular routine ;¥¥ .. ... ... ;¥¥ x word result ;space for the return OSErr ;¥¥ ;¥¥ On dispatch to the user proc, the stack looks like: ;¥¥ ;¥¥ ENTRY: SP-> long loadRTS ;return address to a spot in LoadPickMethod to clean up stuff ;¥¥ 4 D1 - 6 parameters ;the parameters for the particular routine ;¥¥ .. ... ... ;¥¥ x word result ;space for the return OSErr ;¥¥ x + 2 long procHandle ;handle to the user pick proc ;¥¥ x + 6 word procHandState ;the ÒstateÓ of the user proc handle (from HGetState) ;¥¥ x + 8 long mainRTS ;the main return address ;¥¥ ;¥¥ On return from the user proc, the stack looks like: ;¥¥ ;¥¥ EXIT: SP-> word result ;space for the return OSErr ;¥¥ 2 long procHandle ;handle to the user pick proc ;¥¥ 6 word procHandleState ;the ÒstateÓ of the user proc handle (from HGetState) ;¥¥ 8 long mainRTS ;the main return address ;¥¥ ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ CustomPickProcs PROC EXPORT INITPICKMETHOD EXPORT RECORDCOLORS EXPORT CALCCOLORTABLE EXPORT KILLPICKMETHOD ;---------------------------------------------------------------------------------------------------- ExtraStack RECORD 0 procHandle ds.l 1 procHandleState ds.w 1 mainRTS ds.l 1 extraSize ds.l 0 ENDR ;---------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------- INITPICKMETHOD move.l #16,D1 move.l #0,D0 ;the selector for INIT is 0 bra.s UsePickMethod ;---------------------------------------------------------------------------------------------------- RECORDCOLORS move.l #22,D1 move.l #1,D0 ;the selector for RECORDCOLORS is 2 bra.s UsePickMethod ;---------------------------------------------------------------------------------------------------- CALCCOLORTABLE move.l #20,D1 move.l #2,D0 ;the selector for CALCCOLORTABLE is 4 bra.s UsePickMethod ;---------------------------------------------------------------------------------------------------- KILLPICKMETHOD move.l #10,D1 move.l #3,D0 ;the selector for KILL is 6 ; bra.s UsePickMethod ;¥¥¥ WARNING ¥¥¥ Fall thru into LoadPictMethod. ;---------------------------------------------------------------------------------------------------- UsePickMethod move.l (SP)+,A1 ;this is the return address move.w (SP)+,D2 ;this is the color pick method move.l A1,-(SP) ;put the return address back on the stack cmp.w #systemMethod,D2 beq.s @usePopular cmp.w #popularMethod,D2 beq.s @usePopular cmp.w #medianMethod,D2 bne.s LoadPickMethod @useMedian lea MedianDispatch,A0 jmp (A0) @usePopular lea PopularDispatch,A0 jmp (A0) ;---------------------------------------------------------------------------------------------------- WITH ExtraStack LoadPickMethod sub.w #extraSize,SP ;allocate extra space on the stack lea extraSize(SP),A0 move.l SP,A1 move.l D0,-(SP) ;save the selector on the stack move.l D1,D0 _BlockMove move.l (SP)+,D0 ;restore the selector from the stack lea (SP,D1.W),A1 move.l (SP),mainRTS(A1) lea LoaderReturn,A0 move.l A0,(SP) clr.l -(SP) ;space for the resource handle move.l #'cpmt',-(SP) ;the resource type for color pick procs move.w D2,-(SP) ;the ID number for this particular proc move.l D0,D2 ;save the selector in D2 _GetResource move.l (SP)+,D0 ;get the handle into D0 beq.s @error ;if the handle is nil, then we got an error move.l D0,procHandle(A1) ;save the proc handle in the stack frame move.l D0,A0 ;put the handle into A0 _HGetState move.w D0,procHandleState(A1) ;save the state of the handle _MoveHHi _HLock move.l D2,D0 ;put the selector back into D0 so we can dispatch move.l (A0),A0 ;dereference the resource handle into our proc ptr jmp (A0) ;and return all the way to the user routine @error move.l mainRTS(A1),A0 ;move the main return address into A0 lea extraSize(SP,D1.W),SP ;clean off all the stack junk move.w #cantLoadPickMethodErr,-(SP) ;move the error code back on the stack jmp (A0) ;and return all the way to the main routine ENDWITH ;---------------------------------------------------------------------------------------------------- LoaderReturn move.w (SP)+,D1 ;get the OSErr into D1 move.l (SP)+,A0 ;get the procHandle into A0 move.w (SP)+,D0 ;get the procHandleState into D0 move.l (SP)+,A1 ;get the user return address into A1 move.w D1,-(SP) ;put the OSErr back onto the stack move.l A1,-(SP) ;put the user return address back on the stack _HSetState ;restore the procÕs state rts ;..and exit thru the user routine ;---------------------------------------------------------------------------------------------------- ENDPROC ;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ END