mac-rom/QuickDraw/PictUtilities/puLowLevel.a
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

1402 lines
63 KiB
Plaintext

;
; 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