sys7.1-doc-wip/Toolbox/IconUtils/IconUtils.c
2019-07-27 22:37:48 +08:00

6733 lines
228 KiB
C

/*
File: IconUtils.c
Contains: C laguage portions of color icon plotting utilities
Written by: David Collins
Copyright: © 1990-1992 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<SM7> 12/3/92 RB Renamed GetClut to IconGetClut to avoid a conflict.
<78> 11/24/92 KSM Fix ROMMapInsert setting in CubeE version (77+74 problem).
<77> 11/23/92 DTY Make the CubeE part of this file build too.
<76> 11/13/92 JDR Updated QuickDraw.h to not use the dangerous pattern type.
<75> 10/26/92 DTY Replace explicit references to ExpandMem globals with macro API.
<74> 9/03/92 JDR Changed noMaskFound to noMaskFoundErr. Removed private inline
version of myGetHandleSize, which must be glue because the
Memory Manager may return MemErr in D0. Removed a duplicate
define for RomMapInsert. Removed private inline to NewGestalt.
<73> 7/2/92 DC Sigh, rolled back the cool changes I made to the 7.0 versions of
IconUtils because the fix I made makes Finder Printing work
differently.
<72> 7/1/92 DC #1032893 <csd>: Changed the onScreen field of the ParamBlock
structure to useCopyMask. The flag is now true if the current
port has user-defined bottlenecks or is recording to a PICT. No
longer overload this flag to mean "always plot 8-bits to a color
port". The world is now safe for Tsunami and the bug is fixed.
<71> 6/5/92 DC Added registry of the Gestalt selector. (conditionalized for
theFuture)
<70> 3/20/92 DC Added UpdateIconGlobals to reinitialize the icon globals in
response to dynamic changes in the make up of the device list.
Used GetExpandMem to replace the GetGlobalPtr.
<69> 3/19/92 DC Changed references to printerPortCache in the globalIconData
structure to a separate ExpandMem reference.
<68> 10/29/91 DC Rolled out all post-7.0 changes by one huge conditional for
Cube-E.
<67> 8/30/91 DC Changed globals from a handle to a ptr (makes code more readable
and avoids locking and unlocking). Changed the way printing is
detected to avoid screen plotting problems during background
printing. Fixed maskRgn to scale back the region to a rectangle
under low memory conditions.
<66> 8/28/91 DC changed name of ParamBlock to IconParamBlock to avoid confusion
with HFS, fixed problem with alignment of shrunken icons. Fixed
end condition in ApplyPattern loop.
<65> 7/15/91 dba get rid of MemErr, TheGDevice, and fixed up the ShortMul and
ShortDiv functions
<64> 3/11/91 DC sad, #84447: took out conditionalization so that the bug fix
will go into 7.0
<63> 3/7/91 DC ngk, #84447: completely factored out 4-bit selection from 4-bit
labeling in MakeClut. Conditionalized with forPost70
<62> 3/5/91 DC csd,#84160: Fixed memory moves in the middle of purging icon
cluts in SetLabel
<61> 2/27/91 DC dba,#DCC004: Icon cluts made purgeable when built as opposed to
when the purged handles are created
<60> 2/22/91 DC dba - added initials and bug number reference: worksheet number
DCC003
<59> 2/21/91 DC Changed transform and hit-test code to use offscreen bitmaps
instead of region operations. Fixed IconToRgn type code to use
less stack space.
<58> 1/30/91 DC KSM - Fixed Memory moving bug in DoTransform
<57> 1/19/91 DC KSM - In the case of an SICN without a mask (where I synthesize
one) the wrong rowBytes is used on the data when generating a
boundary rectangle. The fix is to use maskMap.baseAddr as the
source data for boundary generation.
<56> 1/16/91 DC Fix Greg and Kip's previous fix to my potential divide-by-zero
error
<55> 1/14/91 KIP <gbm> Fix greg's boneheaded mistake.
<54> 1/14/91 DC KSM - Minor performance shore-ups to improve Finder behavior on
Plus's and SE's
<53> 1/11/91 DC KSM - Fixed 2 bugs in PlotCIconHandle which prevented correct
drawing of CIcons with no one-bit data (which is legal) and/or
pixel values outside the range of specified clut entries (also
legal)
<52> 12/14/90 DC KSM - Use true grays wherever possible in 2-bit mode
<51> 12/13/90 ngk need to include PalettesPriv to get CheckColors (used to be in
Palettes)
<50> 12/6/90 DC ngk - Change one bit to color plots to use darken when plotting
labeled icons. Rolled some color back into 4-bits. (Went a
little overboard on change <47>)
<49> 11/29/90 DC ngk - Fix the crashing bug when a mask with no data is present.
<48> 11/27/90 DC KSM - Create some routines needed for menu manager
<47> 10/30/90 DC ngk - Fix labeling algorithm to look good in 4-bits and
gray-scale (if I can)
<46> 10/24/90 DC VL - Add a routine to brighten colors before using them to tint
labeled icons. Rewrite Darken() to be correct and faster.
Changed MakeClut to cause all colors in a clut to be disabled,
rather than just Paulien's colors.
<45> 10/21/90 gbm (with dba) Made compatible with 3.2 C compiler (and incompatible
with previous compilers).
<45> 10/2/90 JAL Changing #pragmas to 3.2 version.
<44> 9/25/90 DC Made two fixes for revision 42. One fixed a bug with saving and
restoring handle states which caused a locked handle not to
plot. This was stopping Alex Kazim. This change was reviewed by
Scott Boyd. The other was a minor change to the way black and
white systems plotted non-transformed icons in an attempt to
boost performance on the plus and SE. This change was refined in
in revision 43, but time-tests showed that it was not a win.
This revision rolls out the change to the black and white draw,
but keeps the necessary change to the way icon handle states are
saved and restored (by merging 42 with 41)
<43> 9/24/90 DC Fixing plotting to Black and White Macs that I broke on my last
fix.
<42> 9/23/90 DC <ngk> Fixed bug with get and SetState using sign extended data
<41> 9/21/90 DC <ksm>Fixed clip region bug.
<40> 9/15/90 DC Rewrote callbacks as assembly, used GetGray in one-bit color
disabled case
<39> 9/15/90 csd Removed the declaration of DeviceLoop and fixed the call to
match the interfaces.
<38> 9/10/90 DC Fixed minor bug in 1-bit color quickdraw opened case
<37> 9/7/90 DC Fix up appearance of disabled state
<36> 9/6/90 JSM SetLabel was setting the length of the string one too many.
<35> 9/6/90 DC Merge in new labeling/darkening algorithm
<34> 9/1/90 dvb Add CIconToIconSuite routine
<33> 8/29/90 DC allow one-bit versions without masks. I.e. ICON
<32> 8/22/90 DC fixed some low-memory condition bugs
<31> 8/9/90 DC Fixed to prevent hanging in icons that have a mask with empty
data
<30> 8/7/90 DC Temporary fix for bug that caused icons not to plot in low
memory conditions
<29> 8/6/90 DC added a true gray disabled state for black and white icons on
color systems (will not degrade correctly yet)
<28> 8/2/90 DC Changed the transfer mode in CopyBits in Render() from srcCopy
to srcOr (makes 'PICT''s look better)
<27> 7/26/90 DC Fixed some dumb bugs that caused malfunction
<26> 7/25/90 DC Converted some routines to assembly language for efficiency
<25> 7/23/90 DC Added CheckColors, fixed black and white transforms
<24> 7/13/90 dba fixed C warnings
<23> 7/12/90 DC Fixed bug in 4-bit mode. Added support for printing and
offscreen cases. Started to add support for graceful
degradation.
<22> 7/5/90 DC Added hit-testing and region-producing
<21> 6/29/90 DC Fix bad resolution values in SetUpPixMap. Added set up of
global data and colorized labeling.
<20> 6/15/90 DC Get my cluts from a global now (although its none of your
business where I get my cluts from)
<19> 6/13/90 DC Fixed 4-bit clut in MakeIconData
<18> 6/13/90 DC Added MakeIconData and associated types
<17> 6/7/90 ngk auto-include quickdraw
<16> 5/31/90 DC Make some fixes to icon itteration
<15> 5/30/90 DC Removed DisposeIconCache and changed name of interface from
IconUtils.h to Icons.h
<14> 5/22/90 DC Made IconCache a subclass of IconSuite.
<13> 5/9/90 DC Use srcOr mode in stead of transparent mode in color plotting
(faster, more correct)
<12> 5/8/90 DC Fixed alignment problem in icon caches. improved efficiency of
loading heuristic
<11> 5/4/90 DC Added many new routines NewIconSuite, ForEachIconDo, etc.
<10> 4/18/90 DC Use typeTable to map IconType values to corresponding ResType
<9> 4/18/90 DC Fixed drawing in 4-bits and small size
<7> 4/11/90 DC Changed for interface fixes
<6> 4/10/90 DC Fixed bug in ploticoncache - might crash if no icon was
available
<5> 4/10/90 DC Merged PlotIconCache and PlotIconSuite into one routine -
PlotBestIcon
<4> 4/9/90 DC IconCache stuff now is working - isn't life strange and
wonderful
<1> 4/7/90 DC first checked in - IconCache stuff not yet working
To Do:
*/
// this is a big conditional used to protect Cube-E from all my post-7.0 changes. I thought
// this was the safest way to roll back completely to 7.0. The code in the else clause of this
// conditional is the entirety (minus header comments) of version 64 of IconUtils.c, which is
// the version that went into 7.0 final.
#if TheFuture
#ifndef __ICONS__
#include <Icons.h>
#endif
#ifndef __EXPANDMEMPRIV__
#include <ExpandMemPriv.h>
#endif
#ifndef __ERRORS__
#include <Errors.h>
#endif
#ifndef __QUICKDRAW__
#include <QuickDraw.h>
#endif
#ifndef __PALETTES__
#include <Palettes.h>
#endif
#include <IconUtilsPriv.h>
#include <PalettesPriv.h>
#ifndef __MEMORY__
#include <Memory.h>
#endif
#ifndef __RESOURCES__
#include <Resources.h>
#endif
#ifndef __TRAPS__
#include <Traps.h>
#endif
#ifndef __SYSEQU__
#include <SysEqu.h>
#endif
#ifndef __OSUTILS__
#include <OSUtils.h>
#endif
#ifndef __QDOFFSCREEN__
#include <QDOffScreen.h>
#endif
#ifndef __GESTALTEQU__
#include <GestaltEqu.h>
#endif
GrafPtr myGetPort() =
{
0x2255, // MOVEA.L (A5), A1 ; Get Pointer to globals
0x2011 // MOVE.L (A1), D0 ; Return current grafport
};
ConstPatternParam GetBlack() =
{
0x70F0, // MOVEQ.L black, D0
0xD095 // ADD.L GrafGlobals(A5), D0
};
ConstPatternParam GetHalfGray() =
{
0x70E8, // MOVEQ.L gray, D0
0xD095 // ADD.L GrafGlobals(A5), D0
};
ConstPatternParam GetLightGray() =
{
0x70E0, // MOVEQ.L ltGray, D0
0xD095 // ADD.L GrafGlobals(A5), D0
};
#define ResErr (*((short *)0xA60))
#define DeviceList (*((GDHandle *)0x8A8))
#define MainDevice (*((GDHandle *)0x8A4))
#define ROM85 (*(unsigned short *) 0x28E)
#define HaveCQD() (ROM85 <= 0x3fff)
// These are machine language multiply and divide routines, which are
// used to avoid using CRuntime.o
#pragma parameter __D0 ShortMul(__D1, __D0)
long ShortMul(short, short)
= 0xC1C1;
#pragma parameter __D0 ShortDiv(__D0, __D1)
short ShortDiv(short, short)
= 0x81C1;
#define MakeLong(aThing) (*((long *)(&(aThing))))
// This external table is defined in IconUtils.a
extern ResType typeTable[IconSpace];
// typedef for call-back used internally for routines like PlotIconGeneric, HitTestIconGeneric, etc.
typedef Handle (*IconRetriever)(long data, IconType theIcon);
// the folowing is the definition of the parameter block used throughout the code
typedef struct
{
PixMap dataMap; // PixMap (or BitMap) structure used for icon image data
BitMap maskMap; // BitMap used for icon mask data
BitMapPtr dstMap; // Pointer to the destination BitMap
Handle theMask, // Handle to one-bit data (usually with mask appended)
theData; // Handle to 4 or 8 bit icon image data
Rect theRect, // destination rectangle (as modified by alignment)
boundary; // boundary of non-zero data within the maskMap bounds.
short rowBytes; // basic rowBytes of data (2 or 4)
unsigned short theTransform, // transform broken out from transform code
theLabel, // label broken out from transform code
selected, // selected state broken out from transform code
dontDraw, // flag which is set to true for LoadIconCache, cuts operation short before drawing
useCopyMask, // true if the icon does not need to go through bottlenecks.
screenDepth, // used to record the depth of the device we'll be drawing to
dataDepth, // used to record the depth of the data we'll be drawing
clutNumber; // index into an array of CTabHandles, specifies CLUT we will use with dataMap
Boolean useRegions; // true if we will use regions to perform transforms, false normally
IconType theGroup; // size category of icon we are using (mini, small or large) also used to indicate a cicn
long userData; // data for icon-data-getting call back
IconRetriever userProc; // ProcPtr to icon-data-getting call back
CTabHandle theClut; // handle to CLUT we will use with dataMap
GDHandle theDevice; // device we are currently drawing to or NULL if black and white or if single device
unsigned long maskBuffer[32]; // buffer in which we store masks we create for one reason or another
} IconParamBlock;
#define HorizontalCodes 0xC // mask for horizontal positioning codes
#define VerticalCodes 0x3 // mask for vertical positioning codes
short DivByTwoShort(short theValue)
{
if (theValue < 0)
++theValue;
theValue >>= 1;
return theValue;
}
// PerformAlignment offsets theBlock->theRect to cause the icon to have the alignment specified by the caller
pascal void PerformAlignment(IconParamBlock *theBlock, short align)
{
short offsetx,
offsety;
Rect *theRect = &(theBlock->theRect),
theBoundary = theBlock->boundary;
MapRect(&theBoundary, &(theBlock->dataMap.bounds), theRect);
switch (align & HorizontalCodes)
{
case atHorizontalCenter:
offsetx = DivByTwoShort((theRect->right + theRect->left) - (theBoundary.right + theBoundary.left));
break;
case atLeft:
offsetx = theRect->left - theBoundary.left;
break;
case atRight:
offsetx = theRect->right - theBoundary.right;
break;
default:
offsetx = 0;
}
switch (align & VerticalCodes)
{
case atVerticalCenter:
offsety = DivByTwoShort((theRect->bottom + theRect->top) - (theBoundary.bottom + theBoundary.top));
break;
case atTop:
offsety = theRect->top - theBoundary.top;
break;
case atBottom:
offsety = theRect->bottom - theBoundary.bottom;
break;
default:
offsety = 0;
}
OffsetRect(theRect, offsetx, offsety);
}
// The following routines are used to create regions from mask BitMap's for IconToRgn-type calls and
// for hit-testing and transforms when an icon is draw larger than 32-bits. They are faster than the
// more general BitMapRgn code because they are optimized for a bitMap no greater that a longword in
// width.
#define TempRgnBufferSize 100
pascal Boolean AddBufferToRgn(RgnHandle theRgn, unsigned short *rgnBuffer, unsigned short *regionPtr)
{
unsigned long incrementSize = (unsigned long)regionPtr - (unsigned long)rgnBuffer;
short rgnSize = (*theRgn)->rgnSize;
SetHandleSize((Handle)theRgn, rgnSize + incrementSize);
if (MemError() != noErr)
return false;
BlockMoveData((Ptr)rgnBuffer, (Ptr)(((unsigned char *)(*theRgn)) + rgnSize), incrementSize);
(*theRgn)->rgnSize += incrementSize;
return true;
}
pascal void MaskRgn(RgnHandle theRgn,
IconParamBlock *theBlock)
{
unsigned char *data = theBlock->maskMap.baseAddr; // This may or may not be locked down
unsigned short rowBytes = theBlock->maskMap.rowBytes,
rowIndex,
rgnBuffer[TempRgnBufferSize],
*regionPtr = rgnBuffer,
spaceLeftInBuffer = TempRgnBufferSize;
unsigned long sizeMask = (rowBytes > 2) ? 0xFFFFFFFF : 0xFFFF0000,
lastRow = 0;
// initialize output region
(*theRgn)->rgnSize = sizeof(Region);
(*theRgn)->rgnBBox = theBlock->boundary;
data += ShortMul(rowBytes, theBlock->boundary.top); // Point to the top of real data
// find the inversion points row-by-row
for (rowIndex = theBlock->boundary.top;
rowIndex <= theBlock->boundary.bottom;
++rowIndex, data += rowBytes)
{
unsigned long thisRow,
inversion;
if (rowIndex != theBlock->boundary.bottom)
{
thisRow = *((unsigned long *)data);
inversion = thisRow ^ lastRow; // inversion is a bit-map difference between this row and the last
lastRow = thisRow;
}
else
inversion = lastRow; // To finish the region, we compare the last row to nothing (all zeros)
inversion &= sizeMask; // Take only the significant data.
// if there is any difference between this row and the last, we describe this difference as a set of inversion points
if (inversion)
{
short columnIndex = 0;
// I'm about to add two words to the region (the row index and the first column index, so I make one check to see
// if I've got two words left in my buffer. If I don't, I dump my temporary buffer to the output region and free
// up the temp buffer.
if (spaceLeftInBuffer < 2)
{
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
regionPtr = rgnBuffer;
spaceLeftInBuffer = TempRgnBufferSize;
}
*regionPtr++ = rowIndex;
// find the first inversion point (may be the first point in the row)
while (!(inversion & 0x80000000))
{
++columnIndex;
inversion <<= 1;
}
// record the first inversion point.
*regionPtr++ = columnIndex++;
// update our gas guage.
spaceLeftInBuffer -= 2;
// find and record all subsequent inversion points
do
{
unsigned long check = inversion & 0xC0000000;
if (check && (check != 0xC0000000))
{
// check if I need to flush my temp buffer
if (!spaceLeftInBuffer)
{
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
regionPtr = rgnBuffer;
spaceLeftInBuffer = TempRgnBufferSize;
}
*regionPtr++ = columnIndex;
--spaceLeftInBuffer;
}
columnIndex++;
inversion <<= 1;
} while (inversion);
// cap off the row with a row-capping marker.
if (!spaceLeftInBuffer)
{
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
regionPtr = rgnBuffer;
spaceLeftInBuffer = TempRgnBufferSize;
}
*regionPtr++ = 0x7FFF;
--spaceLeftInBuffer;
}
}
if (!spaceLeftInBuffer)
{
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
regionPtr = rgnBuffer;
spaceLeftInBuffer = TempRgnBufferSize;
}
*regionPtr++ = 0x7FFF; // cap off the rgn
// If the region is purely rectangular, minimize it and return it.
if (((*theRgn)->rgnSize == sizeof(Region)) &&
(((unsigned long)regionPtr - (unsigned long)rgnBuffer) == 18) &&
(rgnBuffer[1] == rgnBuffer[5]) &&
(rgnBuffer[2] == rgnBuffer[6]))
{
SetHandleSize((Handle)theRgn, sizeof(Region));
return;
}
// Flush the remainder of the buffer to the region.
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
return;
error:
// This handle had to be at least sizeof(Region) to start with and the error occurred
// white trying to size it up. Therefore, SetHandleSize to sizeof(Region) can only
// be sizing it down or leaving it unchanged. It should always work.
SetHandleSize((Handle)theRgn, sizeof(Region));
(*theRgn)->rgnSize = sizeof(Region);
}
//-------------------------------------------------------------------------------------
// Search utilities.
//-------------------------------------------------------------------------------------
// GetBestData finds the best available data that is appropriate for the destination device.
// Initially, theBlock->dataDepth is set equal to the screen depth (by SetUpParamBlock). On
// exit, theData is set to the best available color data, theClut is set to the appropriate
// CLUT to use, and dataDepth is updated to reflect the depth of the output data. If no color
// data is available or appropriate, theData and theClut are set NULL and dataDepth is set to 1,
// all of which means that this is a one-bit plot. GetClut calls CheckClut to see if the clut
// of the specified depth fits into the current color environments. It returns false if it is not,
// causing us to degrade to a smaller bit-depth.
pascal void GetBestData(IconParamBlock *theBlock)
{
long theData = theBlock->userData,
IconGetClut(IconParamBlock *, short);
short checkDepth = 8,
whichIcon = theBlock->theGroup + largeIcon8; // whichIcon is initialized to the code
// for the deepest data in our size group
long IconGetClut(IconParamBlock *, short);
IconRetriever theProc = theBlock->userProc;
void MakeClut(IconParamBlock *);
do {
if ((theBlock->dataDepth >= checkDepth)
&& IconGetClut(theBlock, checkDepth)
&& (theBlock->theData = (*theProc)(theData, whichIcon)))
{
theBlock->dataDepth = checkDepth;
// if the the clut returned by IconGetClut is purged, build a new one
if (!(*(theBlock->theClut)))
MakeClut(theBlock);
return;
}
checkDepth -= 4;
--whichIcon;
} while (checkDepth);
theBlock->dataDepth = 1;
theBlock->theClut = NULL;
}
// ChooseBestSize maps a destination rectangle size to an icon size category.
short ChooseBestSize(const Rect *theRect)
{
int width = theRect->right - theRect->left,
height = theRect->bottom - theRect->top;
if ((width >= 32) || (height >= 32))
return largeIcon1;
if (height > 12)
return smallIcon1;
return miniIcon1;
}
#define FIXED(a) (long)(((a)<<16))
#define THEPIXMAP ((PixMapPtr)theBlock) // only works if the pixmap is the first element
// SetUpPixMap initializes the necessary fields of dataMap for a color plot.
pascal void SetUpPixMap(IconParamBlock *theBlock)
{
unsigned short depth = theBlock->dataDepth;
THEPIXMAP->baseAddr = *(theBlock->theData);
// we use the pixelSize field to indicate whether or not we already have the dataMap
// initialized for a color plot, and if so to what depth.
if (THEPIXMAP->pixelSize == depth)
return;
THEPIXMAP->pmVersion = 0;
THEPIXMAP->packType = 0;
THEPIXMAP->packSize = 0;
THEPIXMAP->hRes = FIXED(72);
THEPIXMAP->vRes = FIXED(72);
THEPIXMAP->pixelType = 0;
THEPIXMAP->cmpCount = 1;
THEPIXMAP->planeBytes = 0;
THEPIXMAP->pixelSize = depth;
THEPIXMAP->cmpSize = depth;
// if the icon is a true CIcon, the rowBytes could be any arbitrary value. Otherwise,
// we can calculate the proper PixMap rowBytes value from the basic rowBytes and the
// depth.
if (theBlock->theGroup != aCIcon)
THEPIXMAP->rowBytes = ShortMul(theBlock->rowBytes, depth) | 0x8000;
else
THEPIXMAP->rowBytes = ((CIconPtr)(theBlock->userProc))->iconPMap.rowBytes;
THEPIXMAP->pmTable = theBlock->theClut;
}
//-------------------------------------------------------------------------------------
// IconSuite Manipulation routines.
//-------------------------------------------------------------------------------------
// __NewIconSuite is called directly by the dispatcher to implement the NewIconSuite call.
pascal OSErr __NewIconSuite(IconSuiteHandle *theSuite)
{
*theSuite = (IconSuiteHandle)NewHandleClear(sizeof(IconSuite));
return MemError();
}
// __MakeIconCache is called directly by the dispatcher to implement the MakeIconCache call.
pascal OSErr __MakeIconCache(IconCacheHandle *theHandle,
IconGetter makeIcon,
void *yourDataPtr)
{
IconCacheHandle theCache;
if (!(theCache = (IconCacheHandle)NewHandleClear(sizeof(IconCache))))
return MemError();
(*theCache)->theSuite.type = CACHE; // set the suite type to indicate that this is a cache
(*theCache)->userPtr = yourDataPtr; // install the call-back and the data.
(*theCache)->userMethod = makeIcon;
*theHandle = theCache;
return noErr;
}
// EditIconSuite is called by __AddIconToSuite and __GetIconFromSuite (defined in IconUtils.a)
// If isAdd is false, EditIconSuite assumes refCon is a pointer to a Handle and returns the
// entry of type theType from theSuite (possibly NULL). If isAdd is true, the value in refcon
// is added to theSuite in the slot for data of type theType.
pascal OSErr EditIconSuite(long refCon, // Handle or referenct to handle of icon data
IconSuiteHandle theSuite,
ResType theType,
long isAdd)
{
Handle *theSlot = (*theSuite)->table;
long theEnd = (long)(theSlot + IconSpace);
ResType *theTable = typeTable;
do
{
if (theType == *theTable++)
{
if (isAdd)
*theSlot = (Handle)refCon;
else
*((Handle *)refCon) = *theSlot;
return noErr;
}
} while ((long)(++theSlot) < theEnd);
return paramErr;
}
// __ForEachIconDo is called by the trap dispatcher to implement the ForEachIconDo call. The action
// call-back is called for every member of theSuite specified by selector (See ERS for selector values).
pascal OSErr __ForEachIconDo(IconSuiteHandle theSuite,
unsigned long selector,
IconAction action,
void *yourDataPtr)
{
Handle *theTable = (Handle *)((*theSuite)->table);
long end = (long)(theTable + IconSpace); // there are more data registers than address
ResType *theTypes = typeTable;
OSErr rval = noErr;
char theState = HGetState((Handle)theSuite);
HLock((Handle) theSuite);
do
{
short i = IconSizeSpace;
do
{
if (selector & 0x00000001)
if (rval = (*action)(*theTypes, theTable, yourDataPtr))
goto out;
theTable++;
theTypes++;
selector >>= 1;
} while (--i);
selector >>= (8 - IconSizeSpace);
} while (theTable < (Handle *)end);
out:
HSetState((Handle) theSuite, theState);
return rval;
}
// This is the call back used with ForEachIconDo to implement the __GetIconSuite
// call below. A more sophisticated call-back could be used to implement a
// Get1IconSuite or to ensure that GetIconSuite only got icons from a single
// file. A user could do this kind of thing by rewriting GetIconSuite with
// their own call-back.
pascal OSErr GetIconFromResource(ResType theType, Handle *theIcon, void *yourDataPtr)
{
*theIcon = GetResource(theType, (short)yourDataPtr);
return noErr;
}
// Implements the GetIconSuite call. The selector allows only specific members of the
// suite to be asked for.
pascal OSErr __GetIconSuite(Handle *theHandle, short theID, unsigned long selector)
{
if (__NewIconSuite((IconSuiteHandle *)theHandle))
return MemError();
__ForEachIconDo((IconSuiteHandle)(*theHandle), selector, GetIconFromResource, (void *)theID);
return noErr;
}
// Implements the SetSuiteLabel call. This specifies a default label for a suite
// which is used if no label is specified by the transform. Yes, this is currently used.
// Namely, by the process manager for the Apple Menu.
pascal OSErr __SetSuiteLabel(IconSuiteHandle theSuite, unsigned short theLabel)
{
if (theLabel > 7)
return paramErr;
theLabel <<= labelShift;
(*theSuite)->label = theLabel;
return noErr;
}
// Implements the GetSuiteLabel. Just for completeness.
pascal short __GetSuiteLabel(IconSuiteHandle theSuite)
{
return (((*theSuite)->label) >> labelShift);
}
// This is the call back used with ForEachIconDo to implement DisposIconSuite. An icon
// is only disposed if it is not a resource.
pascal OSErr Disposer(ResType , Handle *theIcon, void *)
{
if (*theIcon && (HomeResFile(*theIcon) == -1))
DisposHandle(*theIcon);
*theIcon = NULL;
return noErr;
}
// Implements the DisposeIconSuite call. disposeData indicates whether an attempt should
// be made to dispose of the data in the suite. Only non-resource handles will be disposed.
pascal OSErr __DisposeIconSuite(IconSuiteHandle theSuite, Boolean disposeData)
{
if (disposeData)
(void)__ForEachIconDo(theSuite, svAllAvailableData, Disposer, NULL);
DisposHandle((Handle)theSuite);
return MemError();
}
// These are the call-backs used with all the generic routines (PlotIconGeneric, HitTestIconGeneric and
// IconToRgnGeneric) to implement PlotIconID, PlotIconMethod, PlotIconSuite, HitTestIconID, etc. etc.
// FromMethod and FromResource are defined in IconUtils.a
Handle FromMethod(long data, IconType theIcon);
Handle FromResource(long data, IconType theIcon);
Handle FromSuite(long data, IconType theIcon)
{
IconCachePtr theCache = *((IconCacheHandle)data);
Handle rval = theCache->theSuite.table[theIcon];
// if a suite is a cache (See Cache routines below) and we don't have any data, we call the user's
// call back to get some data.
if (!rval && theCache->theSuite.type)
{
IconGetter theProc = theCache->userMethod;
if (theProc)
{
rval = (*theProc)(typeTable[theIcon], theCache->userPtr);
(*((IconSuiteHandle)data))->table[theIcon] = rval;
}
}
return rval;
}
// AdjustRgn checks the from and to rects and decides whether the region needs mapping,
// offsetting or nothing. It is defined in IconUtils.a and is called by DoRegionTransform
// and HitTestIconGeneric.
extern pascal void AdjustRgn(RgnHandle maskRgn, Rect *from, Rect *to);
// DoRegionTransform performs a transform using region operations. This will be called
// when the useRegions flag is true.
pascal void DoRegionTransform(IconParamBlock *theBlock, short theTransform)
{
RgnHandle patRgn;
Boolean isASuite = (theBlock->theGroup != aCIcon);
PenState saveState;
char maskState;
GetPenState(&saveState);
// If we are not a true CIcon, we need to generate a region from the icon mask. The
// locking of theMask is redundant in the color case and extraneous in the case where
// we synthesize a mask but the complexity of determining exactly whether it needs to be done
// dosen't buy that much.
if (isASuite)
{
maskState = HGetState(theBlock->theMask);
HLock(theBlock->theMask);
patRgn = NewRgn();
MaskRgn(patRgn, theBlock);
AdjustRgn(patRgn, &(theBlock->maskMap.bounds), &(theBlock->theRect));
}
else
patRgn = (RgnHandle)theBlock->userData; // This is used for true CIcon to hold a region equivalent
// to its mask (See SetupParamBlockForCIcon)
switch (theTransform)
{
// makes an outline and fills it with a 25% gray pattern
case ttOpen:
FillRgn(patRgn, GetBlack());
InsetRgn(patRgn, 1, 1);
FillRgn(patRgn, GetLightGray());
break;
// Or's over the data (already drawn) with a 25% gray pattern
case ttOffline:
PenPat(GetLightGray());
PenMode(patOr);
PaintRgn(patRgn);
break;
// Bit-clears the already drawn data with a 50% pattern
case ttDisabled:
PenPat(GetHalfGray());
PenMode(patBic);
PaintRgn(patRgn);
break;
}
SetPenState(&saveState);
if (isASuite)
{
DisposeRgn(patRgn);
HSetState(theBlock->theMask, maskState);
}
}
// The following routines are used by DoBitMapTransform to accomplish the various
// transform effects. They rely on the fact that the theBlock->maskMap describes
// exactly the countours of the shape they are transforming. This is guaranteed
// by PrescaleMask (See below)
// DoOutline takes the pre-scaled maskMap and OR's an outline of the icon
// over destination. It is used to implement the open transform.
void DoOutline(BitMapPtr maskMap, unsigned long *destination)
{
unsigned char *source = (unsigned char *)(maskMap->baseAddr);
unsigned long dataMask = (unsigned long)(-1),
lastRow,
row,
nextRow,
destRow,
checkRow,
setMask;
short end = maskMap->bounds.bottom - 1,
i;
// data mask is used to mask off all but the bits belonging to one row of the mask map.
dataMask <<= (32 - maskMap->bounds.right);
// last row represents the row of mask data above the one we're analyzing. It is initialized
// to zero because logicly, there are zero bits outside the bounds of our maskMap.
lastRow = 0L;
// row is the row of mask data we are currently trying to hollow out.
row = *((unsigned long *)source) & dataMask;
source += maskMap->rowBytes;
// we march through the rows of our mask, hollowing our rows as we go. We only go as far
// as the row before the bottom-most row, since we are only interested in examining rows
// that are samwiched between other rows with data.
for (i = 0; i < end; i++)
{
// destRow will be our hollowed out row. It is initialized to row since we don't know if it
// needs hollowing.
destRow = row;
// nextRow is the row below the one we are examining. We advance our source pointer here. We
// won't be advancing off the icon because we're stopping one row before the last.
nextRow = *((unsigned long *)source) & dataMask;
source += maskMap->rowBytes;
// if row has data above and below it, we examine its bits left-to-right. If any one bit has
// a one-bit to left of it, to the right of it, on top of it and below it, we turn that bit
// off in destRow. It works, really.
if (destRow)
{
if (lastRow && nextRow)
{
setMask = 0x40000000;
for (checkRow = row; checkRow; checkRow <<= 1, setMask >>= 1)
if (((checkRow & 0xE0000000) == 0xE0000000) && (setMask & lastRow) && (setMask & nextRow))
destRow ^= setMask;
}
// here we OR the destRow with the destination data
*destination |= destRow;
}
// advance the destination pointer as well as lastRow and row.
++destination;
lastRow = row;
row = nextRow;
}
// nextRow now contains the data from the last row in the icon. It either has data, in which case
// it gets OR'd in its entirety, or it dosen't and we do nothing.
if (nextRow)
*destination |= nextRow;
}
// ApplyPattern is used to either bitClear the data in theMap with a 50% pattern (for the disabled transform)
// or to OR the data with a 25% pattern (for offline and open).
pascal void ApplyPattern(BitMapPtr theMap, BitMapPtr theMask, Boolean bitClear)
{
unsigned long *mapData = (unsigned long *)(theMap->baseAddr);
unsigned char *maskData = (unsigned char *)(theMask->baseAddr); // we know how wide this buffer is
short rowBytes = theMask->rowBytes,
end = theMask->bounds.bottom;
do {
if (bitClear)
{
*mapData++ &= 0xAAAAAAAA;
*mapData++ &= 0x55555555;
}
else
{
*mapData++ |= (0x88888888 & *((unsigned long *)maskData));
maskData += rowBytes;
*mapData++ |= (0x22222222 & *((unsigned long *)maskData));
maskData += rowBytes;
}
end -= 2;
} while (end > 0);
}
// DoBitMapTransform causes an icon to appear transformed (open, offline or disabled). If onePass is true,
// It means that we are plotting a one-bit icon and we want to combine the transform patterns with the
// icon data off-screen and plot it all at once. If onePass is false, it means we are plotting a color
// icon and that the color data has already been plotted.
pascal void DoBitMapTransform(IconParamBlock *theBlock, short theTransform, Boolean onePass)
{
BitMap transformMap; // BitMap used for transform data.
unsigned long transformBuffer[32], // offscreen area where we will create transform data
*buffPtr = transformBuffer,
*endPtr = buffPtr + 32;
// IsNotPreScaled is true if we did not have to scale the mask prior to this point. We need to know this to decide
// whether to scale the data for one pass attempt and whether we should use CopyMask.
Boolean IsNotPreScaled = (MakeLong(theBlock->maskMap.bounds.bottom) == MakeLong(theBlock->dataMap.bounds.bottom));
// empty out the transformBuffer, since we are OR'ing data over it.
do {
*buffPtr++ = 0L;
} while (buffPtr < endPtr);
// initialize transform BitMap
transformMap.baseAddr = (Ptr)transformBuffer;
transformMap.rowBytes = 4;
transformMap.bounds = theBlock->maskMap.bounds;
// if we are trying to do this plot in one pass, we copy the one-bit data into the transformBuffer. If the mask has
// not been prescaled, the image data data will not need scaling and we can just copy it into the buffer. (this will
// always be the case in the Finder since they prescale their old-style icons into different size categories) Otherwise,
// we will have to stretch the data into transform buffer;
if (onePass && (theTransform != ttOpen))
{
if (IsNotPreScaled)
{
short height = theBlock->dataMap.bounds.bottom;
unsigned char *theData = *(theBlock->theMask);
buffPtr = transformBuffer;
do {
*buffPtr++ = *((unsigned long *)theData);
theData += theBlock->rowBytes;
} while (--height);
}
else // use srcXor because we do not know the foreColor or backColor but we know the destination is all zeros
CopyBits((BitMapPtr)(&(theBlock->dataMap)), &transformMap, &(theBlock->dataMap.bounds), &(transformMap.bounds), srcXor, NULL);
}
// OR in an outline of the mask if the transform is ttOpen
if (theTransform == ttOpen)
DoOutline(&(theBlock->maskMap), transformBuffer);
// if the transform is ttDisabled, bit clear the data with a 50% gray. If it is ttOffline or ttOpen, OR the data with 25% gray
if (theTransform == ttDisabled)
ApplyPattern(&transformMap, &(theBlock->maskMap), true);
else
ApplyPattern(&transformMap, &(theBlock->maskMap), false);
// I'm putting this in a block to take advantage of register re-use
{
Rect *maskRect = &(theBlock->maskMap.bounds);
// if we're plotting a one-bit, transformed icon or if we're plotting an opened icon, we have all the
// data we need in the transform map and we'll try to render that in one shot (although we may have
// to put down the mask separately. See comments below). Otherwise, we have a color transform and
// we assume that the basic icon data has already been rendered and that we're just going to OR over it
// with our transform pattern.
if (onePass || (theTransform == ttOpen))
{
// The choice of which rendering technique to use is based on three things. If you have don't have color
// QuickDraw, CopyMask dosen't stretch bits so you generally can't use it. If you're printing or plotting
// to an open PICT, again CopyMask won't work. If you're mask is pre-scaled, and therefore has different
// bounds than your data, CopyMask won't work (a bug). In general you want to try to use CopyMask because
// it goes down in one-pass and avoids any possible flashing. If you can't use it, you BitClear the mask
// and OR over it with data, which works regardless of fore and backGround colors.
if (HaveCQD() && (theBlock->useCopyMask) && IsNotPreScaled)
CopyMask(&transformMap, &(theBlock->maskMap), theBlock->dstMap, maskRect, maskRect, &(theBlock->theRect));
else
{
CopyBits(&(theBlock->maskMap), theBlock->dstMap, maskRect, &(theBlock->theRect), srcBic, NULL);
CopyBits(&transformMap, theBlock->dstMap, maskRect, &(theBlock->theRect), srcOr, NULL);
}
}
else
CopyBits(&transformMap, theBlock->dstMap, maskRect, &(theBlock->theRect), srcOr, NULL);
}
}
// Render is called by PlotDeep, PlotShallowBlackAndWhite and PlotShallowColor. It uses the same criterion described above
// to decide which rendering technique to use with the main icon image data.
pascal void Render(IconParamBlock *theBlock)
{
Rect *destRect = &(theBlock->theRect);
if (HaveCQD() && (theBlock->useCopyMask) && (MakeLong(theBlock->dataMap.bounds.bottom) == MakeLong(theBlock->maskMap.bounds.bottom)))
CopyMask((BitMapPtr)(&(theBlock->dataMap)), &(theBlock->maskMap), theBlock->dstMap,
&(theBlock->dataMap.bounds), &(theBlock->maskMap.bounds), destRect);
else
{
CopyBits(&(theBlock->maskMap), theBlock->dstMap, &(theBlock->maskMap.bounds), destRect, srcBic, NULL);
CopyBits((BitMapPtr)(&(theBlock->dataMap)), theBlock->dstMap, &(theBlock->dataMap.bounds), destRect, srcOr, NULL);
}
}
// PlotDeep, PlotShallowBlackAndWhite and PlotShallowColor are all called by PlotTheIcon, once it has been decided what kind of data
// we will be plotting. When these routines are called, theBlock has theDevice, theClut and theData set up. The clipRgn has also been
// set up to whatever extent is necessary.
// PlotDeep is used to plot color data to a deep color screen.
pascal void PlotDeep(IconParamBlock *theBlock)
{
ColorSpec foreGround,
backGround;
Handle theData = theBlock->theData;
short theTransform = theBlock->theTransform;
char dataState;
// the CLUT is purgeable and rendering may move memory.
HNoPurge((Handle)(theBlock->theClut));
// if we've gotten as far as calling PlotDeep we know that there's a good, disabled clut for this screen.
// Setting the local copy theTransform to none indicates that we should not attempt a bit cleared disabled
// transform.
if (theTransform == ttDisabled)
theTransform = ttNone;
// if our data is unloaded, we attempt to load it.
if (!(*theData))
LoadResource(theData);
// lock down the image data because CopyBits or CopyMask may move memory (if only rarely)
dataState = HGetState(theData);
HLock(theData);
SetUpPixMap(theBlock); // initialize dataMap with additional fields needed for a PixMap
// Save the color environment
SaveFore(&foreGround);
SaveBack(&backGround);
// Ensure black and white for fore and backGround color
ForeColor(blackColor);
BackColor(whiteColor);
// theTransform is ttOpen, the transform code will do all the work. Otherwise, we render
// the main color image.
if (theTransform != ttOpen)
Render(theBlock);
// If there's a transform, we OR or bit clear over the image in an appropriate way.
if (theTransform)
{
if (theBlock->useRegions)
DoRegionTransform(theBlock, theTransform);
else
DoBitMapTransform(theBlock, theTransform, false);
}
// restore the color environment
RestoreFore(&foreGround);
RestoreBack(&backGround);
HPurge((Handle)(theBlock->dataMap.pmTable));
HSetState(theData, dataState);
}
// PlotShallowBlackAndWhite is used to plot to a one-bit screen (Either a black and white Mac or
// a monitor set to one-bit)
pascal void PlotShallowBlackAndWhite(IconParamBlock *theBlock)
{
short theTransform = theBlock->theTransform;
ColorSpec foreGround,
backGround;
theBlock->dataMap.baseAddr = *(theBlock->theMask);
// If the icon is selected, we invert the current fore and backGround color
if (theBlock->selected)
{
if (HaveCQD())
{
RGBColor RGBFore,
RGBBack;
SaveFore(&foreGround);
SaveBack(&backGround);
GetForeColor(&RGBFore);
GetBackColor(&RGBBack);
RGBForeColor(&RGBBack);
RGBBackColor(&RGBFore);
}
else
{
GrafPtr curPort = myGetPort();
*((long *)(&foreGround)) = curPort->fgColor;
*((long *)(&backGround)) = curPort->bkColor;
ForeColor(*((long *)(&backGround)));
BackColor(*((long *)(&foreGround)));
}
}
// If we have a transform, use the transform code to render the icon and the transform. Otherwise,
// just render the data.
if (theTransform)
{
// If we have to use regions to generate the transformed image, render the icon and modify it
// by the transform. Otherwise, do the operation in one pass with DoBitMapTransform.
if (theBlock->useRegions)
{
if (theTransform != ttOpen)
Render(theBlock);
DoRegionTransform(theBlock, theTransform);
}
else
DoBitMapTransform(theBlock, theTransform, true);
}
else
Render(theBlock);
// If we mucked with the color environment, restore it.
if (theBlock->selected)
{
if (HaveCQD())
{
RestoreFore(&foreGround);
RestoreBack(&backGround);
}
else
{
ForeColor(*((long *)(&foreGround)));
BackColor(*((long *)(&backGround)));
}
}
}
// PlotShallowColor is for plotting one-bit data to a deep screen
pascal void PlotShallowColor(IconParamBlock *theBlock)
{
RGBColor foreColor,
backColor;
ColorSpec foreGround,
backGround;
short screenDepth = theBlock->screenDepth,
theTransform = theBlock->theTransform,
theLabel;
Boolean isDeep = (screenDepth > 4); // Are we deeper than 4-bits?
// this check makes sure that labeling does not happen in gray-scale or in 2-bit mode
if ((screenDepth <= 2) || (theBlock->theDevice && !((*(theBlock->theDevice))->gdFlags & 0x1)))
theLabel = 0;
else
theLabel = theBlock->theLabel;
if (theLabel)
(void)GetLabel(theLabel, &foreColor, NULL);
else
GetForeColor(&foreColor);
if (theTransform == ttOpen)
{
backColor.red = 52266; // It seems strange to hard-code this color.
backColor.green = 52266; // Should it maybe be function of the label
backColor.blue = 65322; // or some other factor? HMMMM?
}
else
GetBackColor(&backColor);
theBlock->dataMap.baseAddr = *(theBlock->theMask);
// This check allows for a pure disabled color to be used on a screen that has one.
// Otherwise, a disabled icon will be Bit-Cleared with a 50% pattern. GetGray has
// the side-effect of setting foreColor to the average of backColor and foreColor.
// It returns false if no good average could be found. The condition should read
// something like "If we're on a an 8-bit monitor or we're on a 4-bit monitor and
// there's no label, try to find a disabled color for this icon. If you can find
// one, use that color as the foreground color and treat the icon as not transformed."
if ((theTransform == ttDisabled) && (isDeep || (!theLabel)) &&
GetGray(theBlock->theDevice, &backColor, &foreColor))
theTransform = ttNone;
// If the icon is selected, set up the color environment to make it appear darkened.
if (theBlock->selected)
{
void Darken(RGBColor *aColor);
// on a four-bit screen, there is almost never an appropriate, darkened color available,
// so we don't try.
if (isDeep)
Darken(&foreColor);
Darken(&backColor);
}
SaveFore(&foreGround);
SaveBack(&backGround);
// Note: There is no guarantee that a good approximation of foreColor and backColor exist
// on the destination device. This is a hole in the whole color protection scheme in this code.
// These colors should probably be validated in some way before they are used, falling back on a
// black & white image.
RGBForeColor(&foreColor);
RGBBackColor(&backColor);
// Use the same criterion as in PlotShallowBlackAndWhite for render the image.
if (theTransform)
{
if (theBlock->useRegions)
{
if (theTransform != ttOpen)
Render(theBlock);
DoRegionTransform(theBlock, theTransform);
}
else
DoBitMapTransform(theBlock, theTransform, true);
}
else
Render(theBlock);
RestoreFore(&foreGround);
RestoreBack(&backGround);
}
// This routine simply decides which of the two PlotShallow flavors to use. It exists for
// historical reasons and should be folded into PlotTheIcon in a future version.
pascal void PlotShallow(IconParamBlock *theBlock)
{
theBlock->dataMap.rowBytes = theBlock->rowBytes;
theBlock->dataMap.pixelSize = 1;
if (theBlock->screenDepth >= 2)
PlotShallowColor(theBlock);
else
PlotShallowBlackAndWhite(theBlock);
}
// PlotTheIcon sets up the IconParamBlock for plotting the icon to the particular screen and sets up
// the clip region as appropriate. It is either called directly by PlotIconGeneric or indirectly
// by way of DeviceLoop. On entry, theBlock has been set up by SetupParamBlock, MakeBoundary has
// been called, alignment has been done and the mask has been prescaled if necessary. PlotTheIcon
// gets the best available data for the screen depth and calls one of the plotting routines above
// to render the data with its transform.
pascal void PlotTheIcon(short screenDepth,
short flags,
GDHandle theDevice,
IconParamBlock *theBlock)
{
Rect boundRect,
sectRect;
RgnHandle saveClip = NULL;
short theTransform = theBlock->theTransform;
// flags will be non-zero if PlotTheIcon was called from DeviceLoop. Flags will be zero if it
// was called directly from PlotIconGeneric which implies a single target device.
if (theDevice && flags)
{
// the following code generates a global rectangle which bounds the icon in its destination
// size.
boundRect = theBlock->boundary;
MapRect(&boundRect, &(theBlock->dataMap.bounds), &(theBlock->theRect));
LocalToGlobal((Point *)&boundRect);
LocalToGlobal((Point *)(&(boundRect.bottom)));
// If this global rectangle dosen't intersect the GDevice were checking, then we won't plot to it.
// Otherwise, we will check if it is only partially on the screen. If it is, we will adjust the
// clipRgn so that only the portion of the icon that appears on the screen will be drawn.
if (SectRect(&((*theDevice)->gdRect), &boundRect, &sectRect))
{
if ((!EqualRect(&sectRect, &boundRect)) && (!theBlock->dontDraw))
{
RgnHandle realClip = myGetPort()->clipRgn;
saveClip = NewRgn();
GetClip(saveClip);
GlobalToLocal((Point *)(&sectRect));
GlobalToLocal((Point *)(&(sectRect.bottom)));
ClipRect(&sectRect);
SectRgn(saveClip, realClip, realClip);
}
}
else
return;
}
theBlock->screenDepth = screenDepth;
theBlock->theDevice = theDevice;
// The following conditional reads - "try to plot the icon as color if A) we are plotting to an 8-bit
// screen and the transform is not ttOpen or B) we are plotting to a 4-bit screen, the icon is not
// selected, and the transform is neither ttOpen nor ttDisabled. Otherwise, treat it as one-bit"
if (((screenDepth > 4) && (theTransform != ttOpen)) ||
((screenDepth == 4) && (!(theBlock->selected)) && (!(theTransform & ttDisabled))))
{
theBlock->dataDepth = screenDepth; // Initialize data depth to screen depth. GetBestData may
GetBestData(theBlock); // change this value.
}
else
{
theBlock->dataDepth = 1;
theBlock->theClut = NULL;
}
// PlotTheIcon is being called from LoadIconCache, we're done loading the data and we run away.
if (theBlock->dontDraw)
return;
if (theBlock->dataDepth >= 4)
PlotDeep(theBlock);
else
PlotShallow(theBlock);
if (saveClip)
{
SetClip(saveClip);
DisposeRgn(saveClip);
}
}
// MakeBoundary creates a rectangle within the bounds of the maskMap (before it is pre-scaled) which
// bounds the non-zero bits of the mask. It returns false if the mask is all zeros.
Boolean MakeBoundary(IconParamBlock *theBlock)
{
unsigned char *ptr = (unsigned char *)(theBlock->maskMap.baseAddr); // <57>
unsigned short mark,
side,
rowBytes = theBlock->maskMap.rowBytes,
height = theBlock->maskMap.bounds.bottom;
unsigned long temp,
checkSum,
sizeMask = (theBlock->rowBytes < 4) ? 0xFFFF0000 : 0xFFFFFFFF;
// Find the first row that has data and call it the top.
for (side = 0; side < height; ++side, ptr += rowBytes)
if (*((unsigned long *)ptr) & sizeMask)
break;
// If no data in the mask return false
if (side == height)
return false;
theBlock->boundary.top = side;
checkSum = 0;
// mark is set equal to the row number of the last row with data. checkSum
// is set to the OR of all the rows.
for (; side < height; ++side, ptr += rowBytes)
if (temp = (*((unsigned long *)ptr) & sizeMask))
{
mark = side;
checkSum |= temp;
}
// the bottom is equal to the row number of the last row with data plus one.
theBlock->boundary.bottom = mark + 1;
// checkSum is shifted left until the most significant bit is one.
for (side = 0; !(checkSum & 0x80000000); checkSum <<= 1, ++side);
// the number of shifts will be the left boundary
theBlock->boundary.left = side;
// checkSum is further shifted left until all the data is gone.
for (; checkSum; checkSum <<= 1, ++side);
// the number of shifts plus the left offset equals the right boundary.
theBlock->boundary.right = side;
return true;
}
// CreateMask generates a mask from some data. It is generated into a buffer
// at the end of theBlock. CalcMask is used to generate the data.
void CreateMask(IconParamBlock *theBlock, long maskByteLength)
{
Ptr maskBuffer = (Ptr)(theBlock->maskBuffer);
short rowBytes = theBlock->maskMap.rowBytes,
height = ShortDiv(maskByteLength, rowBytes);
// This catches the odd case in which an icon is larger than an icon
// but smaller than an icon with a mask. This makes sure we only use
// 32 bits of height so as not to overrun our buffer.
if (height > 32)
height = 32;
CalcMask((Ptr)(*(theBlock->theMask)),
maskBuffer, rowBytes, 4,
height, rowBytes >> 1);
theBlock->maskMap.baseAddr = maskBuffer;
theBlock->maskMap.rowBytes = 4;
}
// SetupParamBlock finds the appropriate size category of icon to be dealing
// with, gets (or synthesizes) the mask for the data, locks all necessary data
// down, sets up the maskMap and some parameters that don't change throughout
// the course of the plot (or make region) call. It returns FALSE if no mask
// (1-bit data) could be found in any size category.
short SetupParamBlock(IconParamBlock *theBlock)
{
short size,
rowBytes,
desired = ChooseBestSize(&(theBlock->theRect));
long maskByteLength;
Handle theMask;
// Find a mask of the right size category
for (size = desired; size <= miniIcon1; size += IconDepthSpace)
if (theMask = (*(theBlock->userProc))(theBlock->userData, size))
goto done;
for (size = desired - IconDepthSpace; size >= largeIcon1; size -= IconDepthSpace)
if (theMask = (*(theBlock->userProc))(theBlock->userData, size))
goto done;
return false;
done:
theBlock->theMask = theMask;
theBlock->theGroup = size;
theBlock->theData = NULL;
// size here means the basic dimension (height) of the icon of specific size
// category. rowBytes here is a basic rowBytes of the one bit data.
if (size == largeIcon1)
{
rowBytes = 4;
size = 32;
}
else
{
rowBytes = 2;
if (size == smallIcon1)
size = 16;
else
size = 12;
}
// the basic rowBytes is stored because maskMap.rowBytes might be changed to 4
// arbitrarily if the mask is synthesized.
theBlock->rowBytes = rowBytes;
// maskMap is setup
theBlock->maskMap.rowBytes = rowBytes;
MakeLong(theBlock->maskMap.bounds) = 0L;
theBlock->maskMap.bounds.bottom = size;
theBlock->maskMap.bounds.right = rowBytes << 3; // this insures that a mini icon is 12X16
// lock down the mask member and remember its state.
if (!(*theMask))
LoadResource(theMask); // theoretically its cool to let LoadResource decide whether the resource is loaded or not
// but LoadResource has been back-patched thrice at the time of this writing and all kinds
// of annoying stuff goes before the simple check of the master pointer. sigh.
// The following does a size check to guess whether an icon has a mask or not. If not,
// a probable mask is synthesized.
rowBytes *= size; // I'm hijacking rowbytes to use as a temp
rowBytes <<= 1;
maskByteLength = GetHandleSize(theMask);
// if there is no mask data appended to the one-bit version of this icon (i.e. its not a proper ICN#, ics#, icm#)
// synthesize a mask. Otherwise, we just point the maskMap baseAddr at the mask data in the one-bit icon.
if (maskByteLength < rowBytes)
{
rowBytes >>= 1;
CreateMask(theBlock, maskByteLength);
}
else
theBlock->maskMap.baseAddr = (*theMask) + (maskByteLength >> 1);
// This is just to allow the Finder to use some SICN's as minis without special-casing (Grrrr!)
if ((theBlock->theGroup == miniIcon1) && (maskByteLength > rowBytes))
{
long center = ((theBlock->theRect.bottom + theBlock->theRect.top) >> 1);
theBlock->theRect.top = center - 8;
theBlock->theRect.bottom = center + 8;
theBlock->maskMap.bounds.bottom = 16;
}
theBlock->dataMap.bounds = theBlock->maskMap.bounds; // the mask and data have the same source bounds (may change later)
theBlock->dataMap.pixelSize = 0; // functions as flag saying "the data map is not yet initialized as a color grafport"
theBlock->useRegions = false; // Initially assume that we will not use regions for transforms and hit testing.
// PrescaleMask may change this value.
return true;
}
// PrescaleMask is used after SetupParamBlock. If the destination rectangle of an icon is
// less than or equal to 32 in both dimensions, and if it is different in height or width
// to maskMap.bounds, we do the stretching that will take place off-screen. The result ends
// up in the maskBuffer and maskMap is modified to point to it. After calling PrescaleMask,
// you are guaranteed that the maskMap describes the countours of the icon's mask as it will
// be drawn (which is important for doing transforms and for hit-testing without doing region
// operations). If an icon is larger than our off-screen buffer (never under 7.0), we use
// region operations for hit-testing and transforms. PrescaleMask is called by HitTestIconGeneric
// and by PlotIconGeneric (only if there is a transform).
pascal void PrescaleMask(IconParamBlock *theBlock)
{
short width = theBlock->theRect.right - theBlock->theRect.left,
height = theBlock->theRect.bottom - theBlock->theRect.top;
if ((width > 32) || (height > 32))
{
theBlock->useRegions = true;
return;
}
// This checks if any stretching will be done… we know that maskMap.bounds has (0,0) for its
// upper left coordinate.
if ((width != theBlock->maskMap.bounds.right) || (height != theBlock->maskMap.bounds.bottom))
{
unsigned long tempBuffer[32];
BitMap tempMap;
Rect newBounds;
MakeLong(newBounds) = 0;
newBounds.right = width;
newBounds.bottom = height;
MakeLong(tempMap.bounds) = 0;
MakeLong(tempMap.bounds.bottom) = 0x00200020;
tempMap.rowBytes = 4;
tempMap.baseAddr = (Ptr)theBlock->maskBuffer;
// if maskBuffer is currently being used, as in the case of a missing mask we have synthesized,
// we dump it temporarily to an intermediate buffer.
if (theBlock->maskMap.baseAddr == theBlock->maskBuffer)
{
theBlock->maskMap.baseAddr = (Ptr)tempBuffer;
BlockMoveData(theBlock->maskBuffer, tempBuffer, 32 * sizeof(unsigned long));
}
CopyBits(&(theBlock->maskMap), &tempMap, &(theBlock->maskMap.bounds), &newBounds, srcCopy, NULL);
MakeLong(theBlock->maskMap.bounds.bottom) = MakeLong(newBounds.bottom);
theBlock->maskMap.baseAddr = (Ptr)theBlock->maskBuffer;
theBlock->maskMap.rowBytes = 4;
}
}
// This is the routine that all plotting goes through. By the time we get to PlotIconGeneric, we
// have a handler routine and some data pushed that identifies how we are going to get suite
// members. For example, in the case of PlotIconID, the refCon is equal to the resource ID and
// the IconRetriever is set to a function call FromResource which calls GetResource for an icon
// type and the given ID. The dontDraw parameter allows for LoadIconCache to act as if it were
// drawing, but in stead just load the data.
//
// PlotIconGeneric is called from assembly language wrappers in IconUtils.a that Implement
// PlotIconID, PlotIconSuite, PlotIconMethod and LoadIconCache.
#define PrintVars (*((short *)0x948))
pascal OSErr PlotIconGeneric(const Rect *theRect,
short align,
short transform,
long refCon,
IconRetriever iconProc,
short dontDraw)
{
GrafPort *curPort;
IconParamBlock theBlock;
GlobalIconDataPtr theGlobals = (GlobalIconDataPtr) GetExpandMemIconCluts();
// if this is a call to LoadIconCache and the input is not an iconcache,
// exit with a paramErr
if (dontDraw && !((*((IconSuiteHandle)refCon))->type))
return paramErr;
// save some basic information in the parameter block
theBlock.theRect = *theRect;
theBlock.userData = refCon;
theBlock.userProc = iconProc;
theBlock.theTransform = transform & transformMask;
theBlock.selected = transform & ttSelected;
theBlock.dontDraw = dontDraw;
curPort = myGetPort();
// useCopyMask is true if the icon does not need to go through bottlenecks
theBlock.useCopyMask = !((curPort->grafProcs) || (curPort->picSave));
theBlock.dstMap = &(curPort->portBits);
if (!SetupParamBlock(&theBlock))
return noMaskFoundErr;
// See MakeBoundary above. If no data was found in the mask portion of the
// one-bit data, then clearly CopyMask would not plot anything. So why bother
// doing any more work? This is not the same case as no one-bit data available.
if (!MakeBoundary(&theBlock))
goto out;
// boundary-making and alignment have to take place before PrescaleMask, because they
// make certain assumptions about the mask which in general are no longer valid after the
// pre-scale.
if (align)
PerformAlignment(&theBlock, align);
// this line performs an off-screen stretching of the mask data if there is a
// a difference between the size of the destination rect and the mask size. Thus,
// the maskMap field will always have a correctly scaled mask of the data which
// can be used for creating transform effects etc. The one exception would be if the
// destination rect was so large that the pre-scaled mask would not fit in the 32 X 32
// buffer in the parameter block. In this case, the useRegions flag would be set and
// regions would be used for transforms, hit-testing etc.
if (theBlock.theTransform)
PrescaleMask(&theBlock);
// If we are plotting to a PixMap, we lock down the mask (because color CopyBits/CopyMask
// can move memory), get label information and decide how to plot based on our screen setup.
if (curPort->portBits.rowBytes < 0)
{
GDHandle theScreen;
Handle theMask = theBlock.theMask;
char maskState = HGetState(theMask);
HLock(theMask);
theBlock.theLabel = (transform & labelMask) >> labelShift;
if (theScreen = theGlobals->virtualScreen)
PlotTheIcon((*((*theScreen)->gdPMap))->pixelSize, 0, theScreen, &theBlock);
else
DeviceLoop(NULL, (DeviceLoopDrawingProcPtr)PlotTheIcon, (long)(&theBlock),
dontMatchSeeds | singleDevices | allDevices);
HSetState(theMask, maskState);
}
else
{
theBlock.theLabel = 0; // If we're not plotting to a CGrafPort, we don't use a label.
PlotTheIcon(1, 0, NULL, &theBlock);
}
out:
return noErr;
}
// IconToRgnGeneric is called by a bunch of assembly language wrappers in IconUtils.a to implement
// IconIDToRgn, IconSuiteToRgn and IconMethodToRgn. It is also called from HitTestIconGeneric if regions
// will be used to perform hit-testing. It takes a valid region, the rect to which the theoretical icon was drawn, the
// alignment used and the same kind of call-back used by PlotIconGeneric above. theRgn is changed to be
// equivalent to the icon's mask when aligned and stretched to the destination rect.
pascal OSErr IconToRgnGeneric(RgnHandle theRgn,
Rect *theRect,
IconAlignmentType align,
long refCon,
IconRetriever iconProc)
{
IconParamBlock theBlock;
// minimal set up for call to SetupParamBlock (I know, its a little squirly that SetupParamBlock needs set up but…)
theBlock.theRect = *theRect;
theBlock.userData = refCon;
theBlock.userProc = iconProc;
// we set up the IconParamBlock, which finds the right-sized mask for the destination rect and loads it. It also
// may synthesize a mask if a SICN or ICON is used for a one-bit icon.
if (!SetupParamBlock(&theBlock))
return noMaskFoundErr;
// if there is no data in the mask, we make an empty region. Makes sense.
if (MakeBoundary(&theBlock))
{
// MaskRgn moves memory so we lock down our mask. Note for future change: a check should be made here
// to see if the mask is in maskBuffer, in which case this lock and restore is extraneous.
Handle theMask = theBlock.theMask;
char maskState = HGetState(theMask);
HLock(theMask);
// If there's an alignment specified, we do it.
if (align)
PerformAlignment(&theBlock, align);
// make the region and stretch it appropriately
MaskRgn(theRgn, &theBlock);
AdjustRgn(theRgn, &(theBlock.maskMap.bounds), &(theBlock.theRect));
HSetState(theMask, maskState);
}
else
SetEmptyRgn(theRgn);
return noErr;
}
// HitTestIconMaskGeneric is called from HitTestIconGeneric (See Below) if regions are not used for the
// hit-testing. It takes the same input as HitTestIconGeneric. The test is performed by stretching the
// mask data off-screen (if necessary) and comparing the point or rect to this data arithmeticly.
#define isAPoint 0
#define isARect 1
pascal Boolean HitTestIconMaskGeneric(unsigned long pointOrRect,
short IsRect,
Rect *theRect,
IconAlignmentType align,
long refCon,
IconRetriever iconProc)
{
IconParamBlock theBlock;
Rect sectRect;
unsigned long testMap;
short theRow,
theCount;
// Set up for call to SetupParamBlock (yeah yeah, I know)
theBlock.theRect = *theRect;
theBlock.userData = refCon;
theBlock.userProc = iconProc;
// If there is no one-bit data or the mask contains nothing but zeros, we return false
if (!SetupParamBlock(&theBlock) || !MakeBoundary(&theBlock))
return false;
// If there is alignment, we do it.
if (align)
PerformAlignment(&theBlock, align);
theRect = &(theBlock.theRect); // we hijak the parameter theRect to point to the possibly shifted destination rectangle
// (NOTE: This is very silly. The idea was to make references to theRect smaller and faster.
// Why not make a new variable, or not do this at all and refer to the rect as theBlock.theRect??
// We luck out under the 3.2 compiler, which sees that we refer to theRect a lot and allocates A3
// for this. If we were out of address registers, this would make larger and slower code)
PrescaleMask(&theBlock); // if the icon is to be shrunken, this routine will do the shrinking offscreen and place the
// result in maskMap
// we set up theRow, theCount and testMap to test against the pre-scaled mask data. theRow is equal to the first row of the maskMap
// that we will test. theCount is equal to the number of rows we will test. testMap is a long-word bitMap which we will intersect
// with each row that we test. If any row has intersection with testMap, then we have a hit.
if (IsRect)
{
// we do the intersection with the possibly shifted rectangle to see if alignment has moved us
// out of the test rect. (if there is no alignment, this is redundant with the SectRect we did
// in HitTestIconGeneric below. This probably needs restructuring in a new version.)
if (!SectRect((Rect *)pointOrRect, theRect, &sectRect))
return false;
// we set test map equal to one scan-line of the rectangle intersection of the test rect and the destination
testMap = 0xffffffff;
testMap <<= (32 - (sectRect.right - sectRect.left));
testMap >>= (sectRect.left - theRect->left);
// theRow is the top of the intersection rectangle in destination co-ordinates.
theRow = (sectRect.top - theRect->top);
// theCount is equal to the height of the intersection
theCount = (sectRect.bottom - sectRect.top);
}
else
{
// testMap is set to a one bit at the horizontal location of the test point in destination co-ordinates
testMap = 1 << (31 - ((short)(pointOrRect & 0xffff) - theRect->left));
// theRow is set equal to the vertical location of the test point in destination co-ordinates
theRow = ((short)(pointOrRect >> 16)) - theRect->top;
// there is only one row to test
theCount = 1;
}
// Here we take theRow, theCount and testMap and compare it to the mask data.
{
short rowBytes = theBlock.maskMap.rowBytes;
// since we treat rows that are sometimes only a word in width as long words, we need
// to mask off the right most data that is significant.
unsigned long significantMask = -1L << (32 - theBlock.maskMap.bounds.right);
unsigned char *maskData = theBlock.maskMap.baseAddr + ShortMul(rowBytes, theRow),
*endData = maskData + ShortMul(rowBytes, theCount);
// start at theRow and loop through theCount rows, testing for itersection with testMap
do {
if (*((unsigned long *)maskData) & significantMask & testMap)
return true;
maskData += rowBytes;
} while (maskData < endData);
}
return false;
}
// HitTestIconGeneric is called by assembly language wrappers in IconUtils.a to implement PtInIconID,
// PtInIconSuite, PtInIconMethod, RectInIconID, RectInIconSuite and RectInIconMethod. It takes a value
// which is either a point or pointer to a rect along with a flag indicating which one it is. It also
// takes a rect which is where the icon being hit-test was drawn and the alignment that was used. Finally,
// the generic call-back and refcon used in all the generic routines (PlotIconGeneric, IconToRgnGeneric, etc.)
// It returns true if the mask data as aligned and stretched intersects either the rect or the pixel to
// the lower right of the point. It returns false if there is no intersection or if any kind of error was
// encountered.
pascal Boolean HitTestIconGeneric(unsigned long pointOrRect,
Rect *theRect,
IconAlignmentType align,
long refCon,
IconRetriever iconProc,
short IsRect)
{
Boolean rval = false;
RgnHandle testRgn = NULL;
Rect sectRect;
if (IsRect)
{
// if the test rectangle does not even touch the destination rect, clearly there is no hit
if (!SectRect((Rect *)pointOrRect, theRect, &sectRect))
goto out;
// if the test rectangle completely engulfs the destination rectangle, clearly there is a
// hit and no further testing is required.
if (EqualRect(theRect, &sectRect))
goto trueOut;
}
else
if (!PtInRect(*((Point *)(&pointOrRect)), theRect)) // If our point is not in the ball-bark, we bail
goto out;
// The only time we would use IconToRgnGeneric to test pointOrRect would be if our maskMap needed scaling
// and we could not do the scaling off-screen with PrescaleMask. This is only true if the height or
// width of the destination rect is greater than 32 (the size of our off-screen buffer). Otherwise, we
// use HitTestIconmaskGeneric which is faster and dosen't move memory as much.
if (((theRect->right - theRect->left) > 32) || ((theRect->bottom - theRect->top) > 32))
{
testRgn = NewRgn();
if (IconToRgnGeneric(testRgn, theRect, align, refCon, iconProc))
goto out;
if (IsRect)
{
if (!RectInRgn((Rect *)pointOrRect, testRgn))
goto out;
}
else
if (!PtInRgn(*((Point *)(&pointOrRect)), testRgn))
goto out;
}
else if (!HitTestIconMaskGeneric(pointOrRect, IsRect, theRect, align, refCon, iconProc))
goto out;
trueOut:
rval = true;
out:
if (testRgn)
DisposeRgn(testRgn);
return rval;
}
// MakeIconData is called after DeviceList is initialized to set up the ExpandMem global structure used by the icon utilities.
// The structure is returned in the variable parameter iconCluts. (Named for historical reasons and not changed lately)
//
// This is the layout of the structure:
//
// #define Labels 8
// #define Sets 8
//
// typedef struct
// {
// Strings labelStrings; // array of eight string handles to label strings. On a black and
// // white Mac, the rest of the structure after this point is not allocated.
// long seeds[Labels * Sets]; // array of seeds for all the cluts (transformed and labeled)
// CTabHandle colorTables[Labels * Sets], // array of CTabHandles for all cluts
// originals[2]; // handles to the two original rom cluts (clut 4 and clut 8)
// RGBColor labelColors[Labels]; // array of 8 label colors
// unsigned char **indexLists[2]; // two handles to arrays of indices used in checking colors
// GDHandle virtualScreen; // a GDHandle used by SetIconDevice and GetIconDevice
// long deviceListSize; // number of GDevices at boot time
// DeviceCheck deviceCheckList[0]; // a variable number of DeviceCheck structures used to cache
// // the results of CheckColors for all GDevices
// } GlobalIconData;
//
// typedef Handle Strings[Labels];
//
// typedef struct
// {
// GDHandle theDevice; // Handle to the GDevice that we're caching the information about
// long theSeed; // the seed of its GDPmap's pmTable at last caching
// unsigned long checkMask, // a bit-map specifying which cluts we actually tried and cached results for
// checkResults, // results of caching as a bit-map
// disabledCheckMask, // the other long word of the CheckMask bit-map needed when we added disabled cluts
// disabledCheckResults; // the other long word of the CheckResults bit-map needed when we added disabled cluts
// } DeviceCheck;
// SetUpDeviceCaches initializes the DeviceCheck structures in the GlobalIconData deviceCheckList array
pascal void SetUpDeviceCaches(GlobalIconData *temp)
{
GDHandle aGDHandle = DeviceList;
DeviceCheck *checkPtr = temp->deviceCheckList;
do {
checkPtr->theDevice = aGDHandle;
checkPtr->theSeed = (*((*((*aGDHandle)->gdPMap))->pmTable))->ctSeed;
checkPtr->checkMask = 0L;
checkPtr->disabledCheckMask = 0L;
++checkPtr;
aGDHandle = (GDHandle)((*aGDHandle)->gdNextGD);
} while (aGDHandle);
}
pascal OSErr __UpdateIconGlobals(void)
{
GlobalIconDataPtr theOldData = (GlobalIconDataPtr) GetExpandMemIconCluts();
GlobalIconDataPtr theNewData;
GDHandle aGDHandle = DeviceList;
short devices = 0;
long size = sizeof(GlobalIconData);
do {
++devices;
size += sizeof(DeviceCheck);
aGDHandle = (GDHandle)((*aGDHandle)->gdNextGD);
} while (aGDHandle);
if (!(theNewData = (GlobalIconDataPtr)NewPtrSysClear(size)))
return MemError();
BlockMoveData((Ptr)theOldData, (Ptr)theNewData, sizeof(GlobalIconData));
DisposPtr((Ptr)theOldData);
theNewData->deviceListSize = devices;
SetUpDeviceCaches(theNewData);
SetExpandMemIconCluts((Ptr) theNewData);
return noErr;
}
pascal OSErr iconGestaltProc(OSType, long *response)
{
*response = 1;
return noErr;
}
pascal void MakeIconData(GlobalIconDataPtr *iconCluts)
{
long size,
devices;
GlobalIconDataPtr temp;
short curResFile, i;
Handle aHandle,
*strPtr;
RGBColor *colorPtr;
GDHandle aGDHandle;
(void)NewGestalt(gestaltIconUtilities, (ProcPtr)iconGestaltProc);
// If we're running on a color machine, allocate the whole structure. Otherwise, just allocate the
// label string list.
if (HaveCQD())
{
aGDHandle = DeviceList;
size = sizeof(GlobalIconData);
devices = 0;
do {
++devices;
size += sizeof(DeviceCheck);
aGDHandle = (GDHandle)((*aGDHandle)->gdNextGD);
} while (aGDHandle);
}
else
size = sizeof(Strings);
temp = (GlobalIconDataPtr)NewPtrSysClear(size);
*iconCluts = temp; // return the structure in the variable parameter
strPtr = &(temp->labelStrings); // point to the beginning of the label string handles
// Get the label strings (Should they be unloaded?)
for (i = labelColorBase; i < (labelColorBase + Labels); ++i)
*strPtr++ = GetResource('lstr', i);
// If we're not on a color machine, we're done.
if (!HaveCQD())
return;
// virtualScreen is initially NULL, indicating that we will traverse the list of devices normally.
temp->virtualScreen = NULL;
// set deviceListSize with the count we generated for allocation
temp->deviceListSize = devices;
// Run through the list of devices, initializing our cache of device information.
SetUpDeviceCaches(temp);
// Save the current resource file and set to the System file.
curResFile = CurResFile();
UseResFile(0);
// Get the two lists of color indices to check at CheckColors time
strPtr = (Handle *)(&(temp->indexLists));
*strPtr++ = GetResource('indl', labelColorBase);
*strPtr = GetResource('indl', labelColorBase + 1);
// Get the two standard system clut's for 8 and 4 bits from ROM
strPtr = (Handle *)(&(temp->originals));
*((short *)RomMapInsert) = mapTrue;
*strPtr++ = GetResource('clut',8);
*((short *)RomMapInsert) = mapTrue;
*strPtr++ = GetResource('clut',4);
// Set the first label color to black.
colorPtr = &(temp->labelColors);
*((long *)(colorPtr)) = 0L;
colorPtr->blue = 0;
++colorPtr;
// Get the other seven label colors from resources
for (i = (labelColorBase + 1); i < (labelColorBase + Labels); ++i)
*colorPtr++ = **((RGBColor **)GetResource('rgb ', i));
// Restore the resource file
UseResFile(curResFile);
// Get the seed for the 8-bit system clut
strPtr = (Handle *)(&(temp->seeds));
*strPtr++ = (Handle)((*(temp->originals[0]))->ctSeed);
// Generate seeds for the other 7 labeled 8-bit cluts and the
// selected clut and the 7 labeled selected 8-bit cluts
for (i = 1; i < (Labels * 2); ++i)
*strPtr++ = (Handle)GetCTSeed();
// Get the see for the 4-bit system clut
*strPtr++ = (Handle)((*(temp->originals[1]))->ctSeed);
// Generate seeds for the other 7 labeled 4-bit cluts and the
// selected 4-bit clut and the 7 labeled selected 4-bit cluts and
// all the other combinations of labeled, selected and disabled cluts
for (i = 1; i < (Labels * 6); ++i)
*strPtr++ = (Handle)GetCTSeed();
// Allocate empty handles for all the cluts
strPtr = (Handle *)(&(temp->colorTables));
for (i = 0; i < (Labels * Sets); ++i)
{
aHandle = NewEmptyHandleSys();
*strPtr++ = aHandle;
}
}
#define acceptableDelta (2 * 256)
// CompareComponent takes two color components and returns 0 if the two are equal within
// the tolerance specified by acceptableDelta, a negative number if the first is less than
// the second by acceptableDelta and a positive number if the first is greater than the
// second by acceptableDelta.
long CompareComponent(unsigned short comp1, unsigned short comp2)
{
long absoluteDifference = (unsigned long)comp1 - (unsigned long)comp2;
if ((absoluteDifference < acceptableDelta) && (absoluteDifference > -acceptableDelta))
return 0;
else
return absoluteDifference;
}
// Darken is used to reduce the brightness of an RGBColor to create selected clut's. The intention
// is to take a color, halve all the components and then increase the saturation of the result by
// halving again all but the strongest component. It is necessary to increase the saturation of
// a color when you halve its components because dividing all the components by two causes
// the difference in component values to become much smaller, decreasing the saturation. The alogrithm
// implemented below causes the HSV value of the color to be halved, while approximately maintaining
// the color's original saturation.
void Darken(RGBColor *aColor)
{
unsigned short *smallest = &(aColor->red),
*notSmallest1 = &(aColor->green),
*notSmallest2 = &(aColor->blue),
*temp = smallest;
long compareValue;
// halve all values
aColor->red >>= 1;
aColor->green >>= 1;
aColor->blue >>= 1;
// Gray has zero saturation, so it can't lose anything in
// the darkening process. Thus we are done in the grayish case.
if (!CompareComponent(*smallest, *notSmallest1) && !CompareComponent(*smallest, *notSmallest2))
return;
// Find the smallest color component.
if (CompareComponent(*smallest, *notSmallest1) > 0)
{
temp = smallest;
smallest = notSmallest1;
notSmallest1 = temp;
}
if (CompareComponent(*smallest, *notSmallest2) > 0)
{
temp = smallest;
smallest = notSmallest2;
notSmallest2 = temp;
}
// Divide its value by two again, so that the larger components are emphasized.
(*smallest) >>= 1;
// take the runner-up in the race for first place and divide it by two again so that
// the largest component is emphasized. If its a tie, we're done.
compareValue = CompareComponent(*notSmallest1, *notSmallest2);
if (compareValue < 0)
(*notSmallest1) >>= 1;
else if (compareValue > 0)
(*notSmallest2) >>= 1;
}
// Luminance takes an RGBColor and generates a value from 0 to 0xffff equal to the percieved
// gray-scale brightness of the color. The algorithm (which is the same one used by the QuickDraw
// to make such conversions) is to take a weighted average of all the components, where the weights
// are 1/8 of the blue component, 5/16 of the red component and 9/16 of the green component. The routine
// is used by Brighten to decide how much to brighten a color.
unsigned long Luminance(RGBColor *aColor)
{
unsigned long temp;
temp = ((unsigned long)aColor->red) << 2; // five times the red component
temp += aColor->red;
temp += ((unsigned long)aColor->green) << 3; // plus nine times the green component
temp += aColor->green;
temp += ((unsigned long)aColor->blue) << 1; // plus two times the blue component
return (temp >> 4); // divide the sum by 16 to get the weighted average
}
// Brighten takes an RGBColor, reduces its saturation and increases its brightness (virtually the inverse
// of Darken). It is used by MakeClut to take a raw label color and convert it into a color which can be
// applied to a clut and used to label 8-bit icons. Brightened colors will not seriously affect the
// brightness of the icon's unselected appearance, but will retain much of the character of the original color.
//
// A little background: A label color is applied to a clut color by multiplying each component by the
// percentage strength of the corresponding label color component. For example, a label color of
// {0x7fff, 0x3fff, 0xffff} would be interpreted as 50% red, 25% green, 100% blue and each clut color
// would have its components multiplied by the corresponding fraction. Since the standard label colors
// vary greatly in their effect on a clut color and since a label color can be customized by the user to
// any value, a label color must be processed so that it does not reduce the brightness of an affected
// icon to the point where it looks selected or unreadably dark. An ideal label color has low HSV saturation
// so that no color component is so attenuated that it becomes very dark. A pure blue for example is
// 100% saturated and would reduce all components except blue to zero, making for a very dark icon. A low
// saturation color has a lot of gray in it, thus allowing some representation from all colors in the icon.
// A good label color should also have a value (in the HSV sense of the word value) of 100%; that is, it should
// have at least one component that is 100%, so that the overall value of the resultant color is not greatly reduced.
// Finally, whatever process is used to turn a bad label color into a good label color should preserve the HSV hue
// of the color, since that is what gives the color its "character". A further constraint on the algorithm to make
// any label color usable is that it be sensitive to the hue. For example, an unprocessed blue label color of a
// certain saturation and value will make an icon look darker than an unprocessed green label color of the same
// saturation and value, because the human eye is many times more sensitive to green than to blue. Thus a blue
// label color needs to have its saturation reduced more than a green label color.
//
// The algorithm: The processing of any arbitrary color into a good label color takes place conceptually in three
// steps (although it it implemented in one pass through the components). The first step is to increase the HSV
// value of the color to 100%, while nearly maintaining the hue. This is done by multiplying each component by a
// factor which takes the maximum component to 0xffff, thus "stretching" the color to its maximum value.
// The second step multiplies the color by a fraction related to the original luminance of the label color. This
// makes the color closer to a gray (i.e. lowers its saturation) and incidentally lowers its over all value (brightness).
// The third step compensates for the incidental loss of brightness by adding in a constant value, which is the complement
// of the compression factor. The maximum component in the resulting color is 100%, the saturation has been lowered to
// an extent related to the luminance and the hue is maintained.
void Brighten(RGBColor *labelColor)
{
unsigned short *component = (unsigned short *)labelColor,
*end = component + 3;
unsigned long temp,
luminance = Luminance(labelColor);
unsigned short basis,
max = labelColor->red;
// Find the maximum component
if (labelColor->green > max)
max = labelColor->green;
if (labelColor->blue > max)
max = labelColor->blue;
// This is the ultimate brightening. Black gets mapped to white. This implements the "feature"
// that specifying black as a label color means "label this with a text only. Don't apply a color
// to this icon." White will in no way affect the appearance of the icon. This was the lowest impact
// change to implement this feature. A more optimal solution in terms of performance would be to
// bail in PlotIconGeneric when we see a black label color. This implies that we should store the
// label color in the parameter block (since we're going to all the trouble of getting it so early on
// in the plot process). This involves some broad changes in labeling code which did not seem warranted
// at B4 time.
if (!max)
{
*((long *)labelColor) = -1;
labelColor->blue = 0xffff;
return;
}
// here we calculate the parameters basis and luminance. luminance is related to the gray-scale brightness
// of the color being brightened, but is compressed to range from 0x6000 to 0xffff. Basis is the complement
// of luminance and ranges from 0x9fff to 0. Calculating the values in this way makes the process of
// brightening sensitive to the color being brightened. If the luminance of the color is low, the basis
// will be large and their will be a greater reduction in saturation.
//
// NOTE: the extent to which luminance is compressed into a smaller range ensures that there is a minimum
// saturation decrease. In the code below, luminance is multiplied by 5/8 and 3/8 of 100% (0x6000) is added
// on to it to achieve the compression. If a fraction smaller the 5/8 is used, the overall saturation of all
// labeled colors will increase. Multiplying luminance by 1/2 and adding 0x8000 produces labeled icons which
// are slightly to dark. Some observers have noted that the current settings produce icons whose labeling
// does not sufficiently suggest the color chosen from the Finder labels menu. This is an area that bears
// some further investigation.
luminance >>= 1; // halve luminance
luminance += (luminance >> 2); // add a quarter of the halved luminance to the halved luminance to get 5/8 luminance
luminance += 0x6000; // add about 3/8 of the whole range to the number
basis = 0xffff - luminance;
// This loop uses the calculated parameters luminance and basic to perform the brightening.
do {
temp = (unsigned long)(*component);
// stretch the color. If the maximum component is already 100%, skip this step for all components
if (max < 0xffff)
{
temp *= 0xffff;
temp /= max; // max will only be zero if the label color is black in which case we will not reach this point
}
// compress the color
temp *= luminance;
temp /= 0xffff;
// add in a gray component
*component++ = basis + (unsigned short)temp;
} while (component < end);
// NOTE: for a future version, the following loop body would have been much better. On the other hand, the current
// version makes the underlying model more clear and performance is not particularly critical in this routine.
//
// temp = (unsigned long)(*component);
//
// if (temp < 0xffff)
// {
// temp *= luminance;
// temp /= max;
// *component = basis + (unsigned short)temp;
// }
//
// ++component;
}
// FindBlack is called by MakeClut to find the lowest luminance color in a clut. Why? It's for the 4-bit labeled true CIcon
// (don't laugh). When such an icon is labeled, the rule is that I turn the black color to the label color. In an Icon
// suite, the black color we want is well-defined as the 15 entry. In a CIcon, there may be no real black, so I'll have to
// choose something to label. Might as well be the closest thing to black. NOTE: a next version of this code should check
// the last entry in the clut to see if it is black, and if so it should return clutSize. If the last entry is black, that's
// the one we want. A good way to accomplish this would be to do the search from the last entry backward, bailing if any
// color is black (something this code should do anyway), taking the minimum luminance value otherwise.
long FindBlack(CTabHandle theClut, long clutSize)
{
ColorSpec *specList = (*theClut)->ctTable;
unsigned long luminance,
minLuminance = 0xffff;
long rval = 0,
index = 0;
do {
luminance = Luminance(&(specList->rgb));
if (luminance < minLuminance)
{
minLuminance = luminance;
rval = clutSize;
}
++index;
++specList;
} while (index <= clutSize);
return rval;
}
// MakeClut is called by CheckClut if IconGetClut passes it a purged clut or by GetBestData if IconGetClut returns a purged clut.
// The clut is reallocated, a copy of the ROM clut of the appropriate bit-depth (4 or 8) is copied into it, it is given
// its old seed back and is then processed according to the transform and label. MakeClut is also called by
// SetupParamBlockForCIcon to process a copy of the CIcon's clut to do transforms.
void MakeClut(IconParamBlock *theBlock)
{
short labelNumber = theBlock->clutNumber; // 0-7, which label, if any, we are using
CTabHandle theClut = theBlock->theClut; // the purged clut handle we are rebuilding
short sourceNumber; // 0 or 1, specifies which of the ROM clut's we will use for a source
unsigned char *listPtr; // pointer to the list of clut indices to be protected with CheckColors
long blackEntry, // index of the color we will consider black for this clut (see FindBlack above)
entryNo,
clutSize; // number of entries in the processed clut
ColorSpec *specList;
RGBColor aColor,
labelColor;
// If we're recreating a purged IconSuite clut, we will use the clut database in our ExpandMem
// globals for the source. If we are processing a copy of a CIcon clut, we get the clut from
// our parameter block.
if (theBlock->theGroup != aCIcon)
{
GlobalIconDataPtr theGlobals = (GlobalIconDataPtr) GetExpandMemIconCluts();
long theSeed = theGlobals->seeds[labelNumber];
CTabHandle source;
unsigned char **theList;
unsigned long size;
// this convoluted arithmetic of getting a labelNumber and sourceNumber from the
// clutNumber (as opposed to just looking at the theLabel field) comes from the fact
// that theLabel specifies what label the user asked for, not necessarily the one
// we're going to give him. For example, if the plotting takes place on a gray screen,
// the label is ignored. The clutNumber reflects this fact, and theLabel does not.
// I could have had more fields in the IconParamBlock to figure all this out, but they would
// have to be set up by IconGetClut. In general, it is more important for IconGetClut to be fast,
// since in most cases, the clut will be already created and cached. We now return you
// to your regularly scheduled programming.
// If the transform is ttDisabled, labelNumber will be in the range of disabled clut numbers.
// This line brings it back into the lower range.
if (theBlock->theTransform == ttDisabled)
labelNumber -= ((Labels * Sets) >> 1);
// If the clut is in the 4-bit range, bring it into the lower range and record the fact that
// we will be building a 4-bit clut.
if (labelNumber >= (Labels << 1))
{
labelNumber -= (Labels << 1);
sourceNumber = 1;
blackEntry = 15; // Only used if we are processing 4-bit cluts. For non-CIcons blackEntry is always 15
}
else
sourceNumber = 0;
labelNumber >>= 1; // There is a selected and non-selected version of every clut. Dividing by two puts
// labelNumber into the range 0-7.
source = theGlobals->originals[sourceNumber]; // get a handle to the source clut
theList = theGlobals->indexLists[sourceNumber]; // get a handle to the list of indices to protect
size = GetHandleSize((Handle)source); // get the size to reallocate the purged clut
clutSize = (*source)->ctSize; // get the number of entries in the clut
ReallocHandle((Handle)theClut, size); // reallocate the clut
HPurge((Handle)theClut); // make clut purgable
BlockMoveData((Ptr)(*source), (Ptr)(*theClut), size); // copy in the source clut
(*theClut)->ctSeed = theSeed; // restore its unique seed
listPtr = *theList; // dereference the index list handle
}
else
{
// a CIcon's clut is copied and processed each time it is plotted (to each screen if on multiple monitors) Thus,
// the values used to determine the parameters used in processing are determined differently.
listPtr = NULL; // CIcon cluts are never checked with CheckColors so listPtr is irrelevant
clutSize = (*theClut)->ctSize; // theClut is already a copy so whatever we take whatever size it is
// For a CIcon, sourceNumber is used only to mean "Shall we process this clut in the 4-bit style (1) or the
// 8-bit style (0)"
if (theBlock->screenDepth < 8)
{
sourceNumber = 1;
blackEntry = FindBlack(theClut, clutSize);
}
else
sourceNumber = 0;
}
// If there's a label get the RGBColor
if (labelNumber)
{
GetLabel(labelNumber, &labelColor, NULL);
// If we're processing an 8-bit clut, we Brighten the color. We don't do it in four bits because there's very little
// hope of achieving the subtle color effects that we can in 8-bits. The labeling algorithm in 4-bits is different
// anyway and does not require the brightening process. (See Brighten above)
if (!sourceNumber)
Brighten(&labelColor);
}
specList = (*theClut)->ctTable; // point to the beginning of the list of ColorSpecs in theClut
// Loop through all the clut entries, doing various kinds of color arithmetic to achieve the
// transformed clut effects.
for (entryNo = 0; entryNo <= clutSize; ++entryNo)
{
// we only do labeling and selection processing on clut entries in the index list. That way,
// an icon designer could decide to use some colors for detail outside this list and not have
// them labeled or selected. Thus, if we have a listPtr, we check if this entry is in the list
// before processing. We process all colors otherwise (for CIcons).
if ((!listPtr) || (entryNo == *listPtr))
{
// If we have a listPtr, increment it.
if (listPtr)
++listPtr;
aColor = specList->rgb; // make a copy of the color to process
// if there is a label color, apply it to this clut entry.
if (labelNumber)
{
// if the clut is 4-bit, set the black entry of the clut to the label color.
// Otherwise, tint the entire clut to the brightened label color by multiplying
// each component of each color by the fractional strength of the corresponding
// component of the label color. The net effect of this process is to "filter"
// the clut entry through the label color.
if (sourceNumber)
{
if (entryNo == blackEntry)
aColor = labelColor;
}
else
{
if (entryNo)
{
// multiplying the components together in this fashion has the effect of "filtering"
// the entry color through the label color.
aColor.red = (aColor.red * (unsigned long)(labelColor.red)) >> 16;
aColor.green = (aColor.green * (unsigned long)(labelColor.green)) >> 16;
aColor.blue = (aColor.blue * (unsigned long)(labelColor.blue)) >> 16;
}
else
{
// if possible, the white entry has to be "brighter" than any other color, so instead of
// multiplying white by the label color (which would simply produce the label color),
// we bias the label color toward white and use that.
aColor.red = ((unsigned long)(aColor.red) + labelColor.red) >> 1;
aColor.green = ((unsigned long)(aColor.green) + labelColor.green) >> 1;
aColor.blue = ((unsigned long)(aColor.blue) + labelColor.blue) >> 1;
}
}
}
// If this clut is for a selected icon, we apply the darkening algorithm to every affected entry
if (theBlock->selected)
Darken(&aColor);
specList->rgb = aColor; // record the processed color
}
else
specList->value |= 4; // If this color is not in the protected list, we set the inhibit bit which causes
// CheckColors to ignore this entry.
// If the transform is ttDisabled, we average the color with white. This has the affect of "bleaching" the color,
// making it seem "grayed" in the same sense that disabled text is shown.
if (theBlock->theTransform == ttDisabled)
{
aColor = specList->rgb;
aColor.red = ((unsigned long)(aColor.red) + 0xffff) >> 1;
aColor.green = ((unsigned long)(aColor.green) + 0xffff) >> 1;
aColor.blue = ((unsigned long)(aColor.blue) + 0xffff) >> 1;
specList->rgb = aColor;
}
++specList; // Get the next entry in the clut
}
}
// GetDeviceCheck is called by IconGetClut to get the DeviceCheck structure corresponding to theDevice.
// The DeviceCheck structure is used to cache information about which cluts have been checked against
// a certain GDevice's available colors.
//
// A little background: CheckColors is a palette manager routine that takes a CTabHandle, a GDevice and some
// tolerance values and returns a boolean, indicating whether the clut's colors can be represented on that device
// to the specified tolerance. To avoid calling CheckColors every time we plot, we cache the results of the
// check in a list of structures stored at the end of our ExpandMem globals. There is one structure for every
// GDevice in the DeviceList. Each structure contains a GDHandle to the corresponding device, the seed of the
// device's color table at the time of the last check and two 64-bit bit-maps, each corresponding to the 64
// different cluts used in plotting color icons. One of the bit-maps indicates whether the clut has been
// checked. The other bit-map indicates the result (good or bad) of the check. Before a clut is returned by
// IconGetClut, the DeviceCheck for the given GDevice is consulted to see if the clut has already been checked
// for that device, and if so, what the result was. If the clut has never before been checked, CheckColors
// is called, the DeviceCheck structure is updated to indicate that the clut has been checked and the
// result is cached.
DeviceCheck *GetDeviceCheck(GDHandle theDevice, GlobalIconDataPtr theGlobals)
{
DeviceCheck *theCheck = &(theGlobals->deviceCheckList[0]); // prime the pointer to the beginning of deviceCheckList
long listSize = theGlobals->deviceListSize,
theSeed = (*((*((*theDevice)->gdPMap))->pmTable))->ctSeed; // Get the device's ColorTable's seed
// Loop through all the DeviceCheck structures in the list, looking for the one corresponding to theDevice
do {
if (theCheck->theDevice == theDevice)
{
// if we find a device, check if its seed has changed since the last check we did. If it has, none of
// the cached check values are valid, so we invalidate the cache and update theSeed.
if (theCheck->theSeed != theSeed)
{
theCheck->theSeed = theSeed;
theCheck->checkMask = 0L;
theCheck->disabledCheckMask = 0L;
}
return theCheck;
}
--listSize;
++theCheck;
} while (listSize);
return NULL;
}
#define iconMaxTol 0x4000
#define iconMaxAveTol 0x2000
// CheckClut takes a parameter block and calls CheckColors on theClut. If theClut is purged,
// we rebuild it first. (NOTE: I don't know why this routine returns a long. Should be a Boolean)
long CheckClut(IconParamBlock *theBlock)
{
// if the clut dosen't exist, make it
if (!(*(theBlock->theClut)))
MakeClut(theBlock);
// the tolerances used here mean that any given color can be represented if the device has a
// color within 25% of it. The average tolerance of all colors in the clut must be within
// 12.5%. These values are empirically derived, but otherwise arbitrary.
return CheckColors(theBlock->theDevice, theBlock->theClut, iconMaxTol, iconMaxAveTol);
}
// IconGetClut gets the clut that should be used to plot an icon described by theBlock using data
// of the theDepth depth. On entry, theBlock has theDevice, theTransform, selected and theLabel
// fields set up. On exit, theBlock has theClut and clutNumber set up. IconGetClut returns false if
// the clut for the icon plotted to theDepth does not pass the CheckColors test for the given
// device. (See GetDeviceCheck above). The basic procedure for finding the clut is to generate
// an index into the array of cluts in the ExpandMem globals from the information in theBlock and
// theDepth. The clut is then checked, using the cached information in the deviceCheckList (See
// GetDeviceCheck above) or CheckClut (see above) if necessary. If the clut passes the test we
// return true, otherwise we return false. (NOTE: again this function should return a boolean
// in stead of a long)
long IconGetClut(IconParamBlock *theBlock, short theDepth)
{
GlobalIconDataPtr theGlobals = (GlobalIconDataPtr) GetExpandMemIconCluts();
short clutNumber; // index into clut array
unsigned long clutMask,
*theCheckMap,
*theResult;
DeviceCheck *theCheck;
GDHandle theDevice = theBlock->theDevice;
// if we are not on a gray-scale monitor, clutNumber is set to twice the label number (because
// there are two cluts for each label, selected and not selected). No labeling is attempted
// on a gray-scale monitor.
if ((theDevice && (!((*theDevice)->gdFlags & 0x1))))
clutNumber = 0;
else
clutNumber = theBlock->theLabel << 1;
// Even numbered cluts are unselected, odd numbered cluts are selected.
if (theBlock->selected)
++clutNumber;
// The second half of the array contains transformed cluts
if (theBlock->theTransform == ttDisabled)
clutNumber += ((Labels * Sets) >> 1);
// the 4-bit cluts follow the 8-bit cluts in the transformed and untransformed groups
if (theDepth < 8)
clutNumber += (Labels << 1);
// Get the clut and record it and its index in theBlock.
theBlock->theClut = theGlobals->colorTables[clutNumber];
theBlock->clutNumber = clutNumber;
// theDevice will be NULL if we are plotting to a printer or to pict. If so,
// we don't do any checks.
if (!theDevice)
return true;
// We get the DeviceCheck structure corresponding to the device we're checking. If GetDeviceCheck
// returns NULL, it means that the caller is plotting to an off-screen GDevice (which is OK and will
// work). We won't have any information cached for such a device so we call CheckClut immediately.
if (theCheck = GetDeviceCheck(theDevice, theGlobals))
{
// get clutNumber into the 0-31 range and set theCheckMap and theResult to either the disabled
// or non-disabled check bit-maps (See GetDeviceCheck above for a general description of
// of these bit-maps)
if (clutNumber >= ((Labels * Sets) >> 1))
{
clutNumber -= ((Labels * Sets) >> 1);
theCheckMap = &(theCheck->disabledCheckMask);
theResult = &(theCheck->disabledCheckResults);
}
else
{
theCheckMap = &(theCheck->checkMask);
theResult = &(theCheck->checkResults);
}
// create a one-bit mask to check against the bit-maps
clutMask = 1 << clutNumber;
// if the clut has never been checked against this device, update theCheckMap, call CheckClut
// and record the result.
if (!((*theCheckMap) & clutMask))
{
(*theCheckMap) |= clutMask;
if (CheckClut(theBlock))
(*theResult) |= clutMask;
else
(*theResult) &= ~clutMask;
}
// if the result of the check was that this clut cannot be well-represented on this device,
// return false
if (!((*theResult) & clutMask))
return false;
}
else
return CheckClut(theBlock);
return true;
}
// __GetLabel is used to get information about the system labels. It is called by the
// trap dispatcher to implement the public GetLabel call. labelNumber specifies which
// label we are querying. labelColor and labelString are buffers into which store the
// label's RGB value and text, respectively. Either labelColor or labelString can be
// NULL to indicate that no information is required about the color or the text.
pascal OSErr __GetLabel(short labelNumber, RGBColor *labelColor,
Str255 labelString)
{
GlobalIconDataPtr theGlobals;
// if labelNumber is out of range we return an error
if ((labelNumber < 0) || (labelNumber > 7))
return paramErr;
// Get a pointer to the global parameter block hanging off of ExpandMem
theGlobals = (GlobalIconDataPtr) GetExpandMemIconCluts();
// If we don't have color QuickDraw, the global parameter block will not
// contain any label color information. (Should this return an error?)
if (labelColor && HaveCQD())
*labelColor = theGlobals->labelColors[labelNumber];
if (labelString)
{
Handle aHandle = theGlobals->labelStrings[labelNumber];
LoadResource(aHandle);
if (ResErr)
return ResErr;
BlockMoveData((Ptr)(*aHandle), (Ptr)labelString, (long)(**aHandle) + 1);
}
return noErr;
}
// InvalLabelCluts is called by SetLabel to purge all the icon-plotting cluts associated with a
// label whose color has been changed
#define EightBitNonSelected 0
#define EightBitSelected 1
#define FourBitNonSelected (Labels * 2)
#define FourBitSelected (FourBitNonSelected + 1)
#define DisabledEightBitNonSelected ((Labels * Sets) / 2)
#define DisabledEightBitSelected (DisabledEightBitNonSelected + 1)
#define DisabledFourBitNonSelected (DisabledEightBitNonSelected + (Labels * 2))
#define DisabledFourBitSelected (DisabledFourBitNonSelected + 1)
pascal void InvalLabelCluts(short labelNumber)
{
CTabHandle *clutPtr;
// Get a pointer to the unselected, non-disabled, 8-bit CTabHandle for this label in the
// global array of icon-plotting cluts and purge it.
clutPtr = &(((GlobalIconDataPtr) GetExpandMemIconCluts())->colorTables[labelNumber << 1]);
EmptyHandle((Handle)(*clutPtr + EightBitNonSelected));
// purge the 8-bit, selected clut
EmptyHandle((Handle)(*(clutPtr + EightBitSelected)));
// purge the 4-bit non-selected clut
EmptyHandle((Handle)(*(clutPtr + FourBitNonSelected)));
// purge the 4-bit selected clut
EmptyHandle((Handle)(*(clutPtr + FourBitSelected)));
// purge the 8-bit, non-selected, disabled clut
EmptyHandle((Handle)(*(clutPtr + DisabledEightBitNonSelected)));
// purge the 8-bit, selected, disabled clut
EmptyHandle((Handle)(*(clutPtr + DisabledEightBitSelected)));
// purge the 4-bit, non-selected, disabled clut
EmptyHandle((Handle)(*(clutPtr + DisabledFourBitNonSelected)));
// purge the 4-bit, selected, disabled clut
EmptyHandle((Handle)(*(clutPtr + DisabledFourBitSelected)));
}
// __SetLabel is used to get information about the system labels. It is called through the
// trap dispatcher to implement the GetLabel call, which is exported but private. labelNumber
// specifies which label we are modifying. labelColor and labelString are buffers containing the
// label's RGB value and text, respectively. Either labelColor or labelString can be
// NULL to indicate that the color or text is not to be changed.
pascal OSErr __SetLabel(short labelNumber, RGBColor *labelColor,
Str255 labelString)
{
GlobalIconDataPtr theGlobals;
Handle aHandle;
// If the labelNumber is out of range, return a paramErr. Note that the range of valid
// label numbers is different for __SetLabel than for __GetLabel. Label 0 cannot be
// changed and is always black with text "None".
if ((labelNumber < 1) || (labelNumber > 7))
return paramErr;
// If labelColor is specified and the global data structure contains color information,
// set the label value.
if (labelColor && HaveCQD())
{
// Update the list of label colors in memory
theGlobals = (GlobalIconDataPtr) GetExpandMemIconCluts();
theGlobals->labelColors[labelNumber] = *labelColor;
// Update the label color in the system file
aHandle = GetResource('rgb ', labelColorBase + labelNumber);
*((RGBColor *)(*aHandle)) = *labelColor;
HNoPurge(aHandle);
ChangedResource(aHandle);
WriteResource(aHandle);
HPurge(aHandle);
// Invalidate cluts associated with labelNumber
InvalLabelCluts(labelNumber);
}
// if there is a label string to set…
if (labelString)
{
unsigned char size = *labelString + 1;
// if the string is larger than a Str32, truncate it
if (size > 32)
size = 32;
// update the string in memory.
theGlobals = (GlobalIconDataPtr) GetExpandMemIconCluts();
aHandle = theGlobals->labelStrings[labelNumber];
LoadResource(aHandle);
BlockMoveData((Ptr)labelString, *aHandle, (long)size);
**aHandle = size-1;
// update the system file
HNoPurge(aHandle);
ChangedResource(aHandle);
WriteResource(aHandle);
HPurge(aHandle);
}
UpdateResFile(0);
return noErr;
}
// These two routines are used as call-backs for PlotIconGeneric to implement
// PlotIconHandle, PlotSICNHandle
Handle FromICON(long data, IconType theIcon)
{
if (theIcon == largeIcon1)
return ((Handle)data);
else
return NULL;
}
Handle FromSICN(long data, IconType theIcon)
{
if ((theIcon == smallIcon1) || (theIcon == miniIcon1))
return ((Handle)data);
else
return NULL;
}
// These two routines are called through the trap dispatcher to implement the public
// calls PlotIconHandle and PlotSICNHandle. NOTE: It was my intention to move these
// and their respective call-backs to IconUtils.a for speed and size efficiency.
pascal OSErr __PlotIconHandle(const Rect *theRect,
short align,
short transform,
Handle theIcon)
{
return PlotIconGeneric(theRect, align, transform, (long)theIcon, FromICON, false);
}
pascal OSErr __PlotSICNHandle(const Rect *theRect,
short align,
short transform,
Handle theIcon)
{
return PlotIconGeneric(theRect, align, transform, (long)theIcon, FromSICN, false);
}
// The QuickDraw.h definition of a CIcon includes a one-byte array at the end as a place-holder
// for the variable-sized mask data. This is a CIcon definition that includes only the fixed-size
// data for a sizeof() operator.
typedef struct
{
PixMap iconPMap; /*the icon's pixMap*/
BitMap iconMask; /*the icon's mask*/
BitMap iconBMap; /*the icon's bitMap*/
Handle iconData; /*the icon's data*/
} myCIcon;
// SetupParamBlockForCIcon initializes a parameter block for a true CIcon. It is called by
// __PlotCIconHandle after the theBlock has its theRect, userProc, theTransform, theLabel
// selected and dontDraw fields initialized. On exit theBlock is setup to be called by
// PlotTheCIcon (See below).
void SetupParamBlockForCIcon(IconParamBlock *theBlock)
{
// the userProc field is used to hold a pointer to the locked CIcon structure
CIconPtr iconPtr = (CIconPtr)(theBlock->userProc);
short height = iconPtr->iconMask.bounds.bottom - iconPtr->iconMask.bounds.top;
unsigned long maskDataSize = (iconPtr->iconMask.rowBytes * height);
// oneBitImageHandle is a Handle which we will set up to take the place of the one-bit
// member of an icon suite. We will be setting up maskMap to point to the mask data
// within the CIcon itself, so we only need to make oneBitImageHandle as big as the
// one-bit image data.
Handle oneBitImageHandle = NewHandle(maskDataSize);
// theOneBitData points to the one-bit data (one-bit image and mask) for this CIcon.
// oneBitPtr is the beginning of the one-bit image data. It is initialized to theOneBitData
// because it is legal to omit the one-bit image data from a CIcon, in which case we use
// the mask as the one-bit data.
unsigned char *theOneBitData = (unsigned char *)(iconPtr->iconMaskData),
*oneBitPtr = theOneBitData;
RgnHandle maskRgn = NewRgn();
GrafPtr curPort = myGetPort();
HLock(oneBitImageHandle);
// If there is one bit-data, we point oneBitPtr to it. Otherwise, we leave it pointing
// to the mask data.
if (theBlock->userData > (sizeof(myCIcon) + maskDataSize))
oneBitPtr += maskDataSize;
// copy the one-bit image data to the simulated one-bit suite entry and point the theMask
// field in the IconParamBlock to point at it.
BlockMoveData((Ptr)oneBitPtr, *oneBitImageHandle, maskDataSize);
theBlock->theMask = oneBitImageHandle;
// copy the bounds and rowBytes information from the CIcon's iconMask into theBlock->maskMap
// and point the baseAddr field to the mask data within the CIcon
BlockMoveData((Ptr)(&(iconPtr->iconMask)), (Ptr)(&(theBlock->maskMap)), sizeof(BitMap));
theBlock->maskMap.baseAddr = (Ptr)theOneBitData;
// copy all the PixMap information from the CIcon into the PixMap within theBlock and set
// the theData field to point to the color data from the CIcon.
BlockMoveData((Ptr)(&(iconPtr->iconPMap)), (Ptr)(&(theBlock->dataMap)), sizeof(PixMap));
theBlock->theData = iconPtr->iconData;
// We use BitMapToRegion to generate the mask region used in doing transforms, etc.
// From this region we also derive our boundary information. userData is overloaded for
// CIcons to contain the handle to the mask region, since unlike icon suites, this region
// is generated once as opposed to when needed.
BitMapToRegion(maskRgn, &(theBlock->maskMap));
theBlock->boundary = (*maskRgn)->rgnBBox;
theBlock->userData = (long)maskRgn;
// Initialize miscelaneous fields for plotting
theBlock->useCopyMask = !((curPort->grafProcs) || (curPort->picSave));
theBlock->dstMap = &(curPort->portBits);
theBlock->dataMap.pixelSize = 0;
theBlock->theClut = NULL;
theBlock->rowBytes = theBlock->maskMap.rowBytes;
theBlock->useRegions = true; // we don't have any choice with CIcon's
}
// myColorTable is a ColorTable with only the fixed-sized portion for a sizeof() operator
typedef struct
{
long ctSeed; /*unique identifier for table*/
short ctFlags; /*high bit: 0 = PixMap; 1 = device*/
short ctSize; /*number of entries in CTTable*/
} myColorTable;
// CopyClut is called by GetCIconClut to create a clut that is the same as input clut but
// padded to the depth of the pixMap data to which it belongs. Thus a 5-entry clut for a
// 4-bit data pixMap would be padded to 16 entries. If a clut needs padding, the entries
// not specified are set to a ramp between white and black. This is to simulate the fact
// that relative colors in a CIcon are assigned to a ramp between foreGround and backGround.
// Since we always set foreGround to black and backGround to white, this will have the desired effect.
CTabHandle CopyClut(CTabHandle theClut, short depth)
{
long specArraySize = sizeof(ColorSpec) << depth; // size of the CSpecArray in new clut
CTabHandle theNewClut = (CTabHandle)NewHandle(sizeof(myColorTable) + specArraySize);
CTabPtr oldClutPtr = *theClut,
newClutPtr = *theNewClut;
short newClutEntries = (1 << depth) - 1, // ctSize of new clut
oldClutEntries = oldClutPtr->ctSize; // ctSize of old clut
newClutPtr->ctSeed = GetCTSeed(); // get unique seed
newClutPtr->ctSize = newClutEntries; // initialize ctSize entry
newClutPtr->ctFlags = oldClutPtr->ctFlags; // copy flags from old clut to new
// copy common data between the two cluts (yeah, and some garbage as well)
BlockMoveData((Ptr)(oldClutPtr->ctTable), (Ptr)(newClutPtr->ctTable), specArraySize);
// if there is no difference in CSpecArray size, we are done now.
if (newClutEntries > oldClutEntries)
{
ColorSpec tempColor,
*theTable = newClutPtr->ctTable;
*((long *)(&(tempColor.rgb))) = 0; // set the last entry to black
tempColor.rgb.blue = 0;
tempColor.value = newClutEntries;
theTable[newClutEntries] = tempColor;
++oldClutEntries; // we are now looking one entry beyond the
// last entry in the old clut
if (newClutEntries > oldClutEntries) // Is there more than one extra spot to fill?
{
long index;
unsigned short increment = 0xffff / (newClutEntries - oldClutEntries),
value = 0;
*((long *)(&(tempColor.rgb))) = -1; // set entry after the last specified to white
tempColor.rgb.blue = 0xffff;
tempColor.value = oldClutEntries;
theTable[oldClutEntries] = tempColor;
// fill all intermediate entries with an evenly spaced ramp from white to black
for (index = newClutEntries - 1; index > oldClutEntries; --index)
{
value += increment;
tempColor.rgb.red = value;
tempColor.rgb.green = value;
tempColor.rgb.blue = value;
tempColor.value = index;
theTable[index] = tempColor;
}
}
}
return theNewClut;
}
// This is the CIcon couterpart to IconGetClut. It is called by PlotTheCIcon (Below) to get
// the appropriate clut to use for plotting to deep devices.
pascal void GetCIconClut(IconParamBlock *theBlock, GDHandle theDevice, short flags)
{
CTabHandle theClut = ((CIconPtr)(theBlock->userProc))->iconPMap.pmTable;
short clutDepth = ((CIconPtr)(theBlock->userProc))->iconPMap.pixelSize,
clutNumber;
// copy the CIcon's clut and pad if necessary
theBlock->theClut = CopyClut(theClut, clutDepth);
// clutNumber is either set to the label number or zero if we're on a gray-scale device.
// MakeClut (See above) uses this value differently for CIcons than for icon suites
if (theDevice && (!(flags & 0x1)))
clutNumber = 0;
else
clutNumber = theBlock->theLabel;
theBlock->clutNumber = clutNumber;
// process the copied clut appropriately for our device, transform and label
MakeClut(theBlock);
}
// PlotTheCIcon is called directly by __PlotCIconHandle (Below) or indirectly via DeviceLoop.
// It is responsible for deciding whether the color icon data will be plotted or the one-bit data.
// When PlotTheCIcon is called from DeviceLoop, we make use of DeviceLoop's setting up of visRgn to
// clip the image appropriately. (In PlotTheIcon, we do our own clipping and only use DeviceLoop
// to iterate through the active devices.)
pascal void PlotTheCIcon(short screenDepth,
short flags,
GDHandle theDevice,
IconParamBlock *theBlock)
{
theBlock->screenDepth = screenDepth;
theBlock->theDevice = theDevice;
// if our monitor is deeper than four bits, or if our monitor is four bits and our icon is
// not selected or disabled, then we can plot the deep data. Otherwise we plot the one-bit
// data.
if (((screenDepth > 4) && (theBlock->theTransform != ttOpen)) ||
((screenDepth == 4) && (!(theBlock->selected)) && (!(theBlock->theTransform & ttDisabled))))
{
theBlock->dataDepth = ((CIconPtr)(theBlock->userProc))->iconPMap.pixelSize; // set up dataDepth for PlotDeep
GetCIconClut(theBlock, theDevice, flags); // get a processed copy of the clut
PlotDeep(theBlock); // plot the icon
DisposHandle((Handle)(theBlock->theClut)); // dispose of our copy of the clut
theBlock->theClut = NULL;
}
else
{
theBlock->dataDepth = 1;
theBlock->theClut = NULL;
PlotShallow(theBlock);
}
}
// __PlotCIconHandle is called through the trap dispatcher to implement the PlotCIconHandle call. It allows
// a CIcon to be plotted, using the modifiers for an icon suite.
pascal OSErr __PlotCIconHandle(const Rect *theRect,
short align,
short transform,
CIconHandle theIcon)
{
IconParamBlock theBlock;
char iconState;
// CIcon's don't exist on non-color machines.
if (!HaveCQD())
return paramErr;
// Since PlotCIconHandle is slower than PlotCIcon, we fall through to PlotCIcon when there is no alignment
// or transform. For visual consistancy, we set fore and back colors to black and white, because PlotDeep
// (See above) does that before plotting deep data.
if ((!align) && (!transform))
{
ColorSpec foreGround,
backGround;
SaveFore(&foreGround);
SaveBack(&backGround);
ForeColor(blackColor);
BackColor(whiteColor);
PlotCIcon(theRect, theIcon);
RestoreFore(&foreGround);
RestoreBack(&backGround);
return noErr;
}
// Lock down the CIcon while we plot it.
iconState = HGetState((Handle)theIcon);
HLock((Handle)theIcon);
// set up for call to SetupParamBlockForCIcon
theBlock.theGroup = aCIcon;
theBlock.userData = GetHandleSize((Handle)theIcon); // Going into SetupParamBlockForCIcon, userData is the size of theIcon,
// coming out, it is the mask region.
theBlock.theRect = *theRect;
theBlock.userProc = (IconRetriever)(*theIcon);
theBlock.theTransform = transform & transformMask;
theBlock.theLabel = (transform & labelMask) >> labelShift;
theBlock.selected = transform & ttSelected;
theBlock.dontDraw = false;
SetupParamBlockForCIcon(&theBlock);
// do alignment, if any
if (align)
PerformAlignment(&theBlock, align);
// map the region to the size of the destination rect
AdjustRgn((RgnHandle)(theBlock.userData),
&(theBlock.maskMap.bounds),
&(theBlock.theRect));
// If we're plotting to a BitMap as opposed to a PixMap, we call PlotTheCIcon directly with a depth of 1.
// Otherwise, we need to to decide more carefully how to plot.
if (theBlock.dstMap->rowBytes < 0)
{
GlobalIconDataPtr theGlobals = (GlobalIconDataPtr) GetExpandMemIconCluts();
GDHandle theScreen;
// If we're assuming we're plotting to a given monitor, we call PlotTheCIcon directly for that
// monitor. Otherwise, we call DeviceLoop and let PlotTheCIcon decide for itself how to plot
// the data on monitor-by-monitor basis.
if (theScreen = theGlobals->virtualScreen)
PlotTheCIcon((*((*theScreen)->gdPMap))->pixelSize, 0, theScreen, &theBlock);
else
DeviceLoop((RgnHandle)(theBlock.userData),
(DeviceLoopDrawingProcPtr)PlotTheCIcon,
(long)(&theBlock), 0);
}
else
PlotTheCIcon(1, 1, NULL, &theBlock);
// restore the handle state of the CIconHandle
HSetState((Handle)theIcon, iconState);
// dispose of the mask region
DisposeRgn((RgnHandle)(theBlock.userData));
// dispose of the one-bit data handle we created in SetupParamBlockForCIcon
DisposHandle((Handle)(theBlock.theMask));
}
#else
#ifndef __ICONS__
#include <Icons.h>
#endif
#ifndef __ERRORS__
#include <Errors.h>
#endif
#ifndef __QUICKDRAW__
#include <QuickDraw.h>
#endif
#ifndef __PALETTES__
#include <Palettes.h>
#endif
#include <IconUtilsPriv.h>
#include <PalettesPriv.h>
#ifndef __MEMORY__
#include <Memory.h>
#endif
#ifndef __RESOURCES__
#include <Resources.h>
#endif
#ifndef __TRAPS__
#include <Traps.h>
#endif
#ifndef __SYSEQU__
#include <SysEqu.h>
#endif
#ifndef __OSUTILS__
#include <OSUtils.h>
#endif
#ifndef __QDOFFSCREEN__
#include <QDOffScreen.h>
#endif
GrafPtr myGetPort() =
{
0x2255, // MOVEA.L (A5), A1 ; Get Pointer to globals
0x2011 // MOVE.L (A1), D0 ; Return current grafport
};
ConstPatternParam GetBlack() =
{
0x70F0, // MOVEQ.L black, D0
0xD095 // ADD.L GrafGlobals(A5), D0
};
ConstPatternParam GetHalfGray() =
{
0x70E8, // MOVEQ.L gray, D0
0xD095 // ADD.L GrafGlobals(A5), D0
};
ConstPatternParam GetLightGray() =
{
0x70E0, // MOVEQ.L ltGray, D0
0xD095 // ADD.L GrafGlobals(A5), D0
};
#pragma parameter __D0 myGetHandleSize (__A0)
long myGetHandleSize(Handle theHandle) = _GetHandleSize;
#define MemErr (*((short *)0x220))
#define ResErr (*((short *)0xA60))
#define DeviceList (*((GDHandle *)0x8A8))
#define TheGDevice (*((GDHandle *)0xCC8))
#define ROM85 (*(unsigned short *) 0x28E)
#define HaveCQD() (ROM85 <= 0x3fff)
#pragma parameter __D0 ShortMul (__D1,__D0)
long ShortMul(unsigned short x, unsigned short y) = 0xc0c1;
#pragma parameter __D0 ShortDiv (__D0, __D1)
short ShortDiv(unsigned long x, unsigned short y) = 0x80c1;
#define MakeLong(aThing) (*((long *)(&(aThing))))
// This external table is defined in IconUtils.a
extern ResType typeTable[IconSpace];
// typedef for call-back used internally for routines like PlotIconGeneric, HitTestIconGeneric, etc.
typedef Handle (*IconRetriever)(long data, IconType theIcon);
GlobalIconDataPtr GetGlobalPtr();
GlobalIconDataHandle GetGlobalHandle();
// the folowing is the definition of the parameter block used throughout the code
typedef struct
{
PixMap dataMap; // PixMap (or BitMap) structure used for icon image data
BitMap maskMap; // BitMap used for icon mask data
BitMapPtr dstMap; // Pointer to the destination BitMap
Handle theMask, // Handle to one-bit data (usually with mask appended)
theData; // Handle to 4 or 8 bit icon image data
Rect theRect, // destination rectangle (as modified by alignment)
boundary; // boundary of non-zero data within the maskMap bounds.
short rowBytes; // basic rowBytes of data (2 or 4)
unsigned short theTransform, // transform broken out from transform code
theLabel, // label broken out from transform code
selected, // selected state broken out from transform code
dontDraw, // flag which is set to true for LoadIconCache, cuts operation short before drawing
onScreen, // true if the icon is being drawn to a video device, false if printing or offscreen destination
screenDepth, // used to record the depth of the device we'll be drawing to
dataDepth, // used to record the depth of the data we'll be drawing
clutNumber; // index into an array of CTabHandles, specifies CLUT we will use with dataMap
Boolean useRegions; // true if we will use regions to perform transforms, false normally
IconType theGroup; // size category of icon we are using (mini, small or large) also used to indicate a cicn
long userData; // data for icon-data-getting call back
IconRetriever userProc; // ProcPtr to icon-data-getting call back
CTabHandle theClut; // handle to CLUT we will use with dataMap
GDHandle theDevice; // device we are currently drawing to or NULL if black and white or if single device
unsigned long maskBuffer[32]; // buffer in which we store masks we create for one reason or another
} ParamBlock;
// PerformAlignment offsets theBlock->theRect to cause the icon to have the alignment specified by the caller
pascal void PerformAlignment(ParamBlock *theBlock, short align)
{
short offsetx,
offsety;
Rect *theRect = &(theBlock->theRect),
theBoundary = theBlock->boundary;
MapRect(&theBoundary, &(theBlock->dataMap.bounds), theRect);
switch (align & 0xC)
{
case atHorizontalCenter:
offsetx = ((theRect->right + theRect->left) - (theBoundary.right + theBoundary.left)) >> 1;
break;
case atLeft:
offsetx = theRect->left - theBoundary.left;
break;
case atRight:
offsetx = theRect->right - theBoundary.right;
break;
default:
offsetx = 0;
}
switch (align & 0x3)
{
case atVerticalCenter:
offsety = ((theRect->bottom + theRect->top) - (theBoundary.bottom + theBoundary.top)) >> 1;
break;
case atTop:
offsety = theRect->top - theBoundary.top;
break;
case atBottom:
offsety = theRect->bottom - theBoundary.bottom;
break;
default:
offsety = 0;
}
OffsetRect(theRect, offsetx, offsety);
}
// The following routines are used to create regions from mask BitMap's for IconToRgn-type calls and
// for hit-testing and transforms when an icon is draw larger than 32-bits. They are faster than the
// more general BitMapRgn code because they are optimized for a bitMap no greater that a longword in
// width.
#define TempRgnBufferSize 100
pascal Boolean AddBufferToRgn(RgnHandle theRgn, unsigned short *rgnBuffer, unsigned short *regionPtr)
{
unsigned long incrementSize = (unsigned long)regionPtr - (unsigned long)rgnBuffer;
short rgnSize = (*theRgn)->rgnSize;
SetHandleSize((Handle)theRgn, rgnSize + incrementSize);
if (MemErr)
return false;
BlockMove((Ptr)rgnBuffer, (Ptr)(((unsigned char *)(*theRgn)) + rgnSize), incrementSize);
(*theRgn)->rgnSize += incrementSize;
return true;
}
pascal void MaskRgn(RgnHandle theRgn,
ParamBlock *theBlock)
{
unsigned char *data = theBlock->maskMap.baseAddr; // This may or may not be locked down
unsigned short rowBytes = theBlock->maskMap.rowBytes,
rowIndex,
rgnBuffer[TempRgnBufferSize],
*regionPtr = rgnBuffer,
spaceLeftInBuffer = TempRgnBufferSize;
unsigned long sizeMask = (rowBytes > 2) ? 0xFFFFFFFF : 0xFFFF0000,
lastRow = 0;
// initialize output region
(*theRgn)->rgnSize = sizeof(Region);
(*theRgn)->rgnBBox = theBlock->boundary;
data += ShortMul((unsigned short)rowBytes, (unsigned short)(theBlock->boundary.top)); // Point to the top of real data
// find the inversion points row-by-row
for (rowIndex = theBlock->boundary.top;
rowIndex <= theBlock->boundary.bottom;
++rowIndex, data += rowBytes)
{
unsigned long thisRow,
inversion;
if (rowIndex != theBlock->boundary.bottom)
{
thisRow = *((unsigned long *)data);
inversion = thisRow ^ lastRow; // inversion is a bit-map difference between this row and the last
lastRow = thisRow;
}
else
inversion = lastRow; // To finish the region, we compare the last row to nothing (all zeros)
inversion &= sizeMask; // Take only the significant data.
// if there is any difference between this row and the last, we describe this difference as a set of inversion points
if (inversion)
{
short columnIndex = 0;
// I'm about to add two words to the region (the row index and the first column index, so I make one check to see
// if I've got two words left in my buffer. If I don't, I dump my temporary buffer to the output region and free
// up the temp buffer.
if (spaceLeftInBuffer < 2)
{
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
regionPtr = rgnBuffer;
spaceLeftInBuffer = TempRgnBufferSize;
}
*regionPtr++ = rowIndex;
// find the first inversion point (may be the first point in the row)
while (!(inversion & 0x80000000))
{
++columnIndex;
inversion <<= 1;
}
// record the first inversion point.
*regionPtr++ = columnIndex++;
// update our gas guage.
spaceLeftInBuffer -= 2;
// find and record all subsequent inversion points
do
{
unsigned long check = inversion & 0xC0000000;
if (check && (check != 0xC0000000))
{
// check if I need to flush my temp buffer
if (!spaceLeftInBuffer)
{
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
regionPtr = rgnBuffer;
spaceLeftInBuffer = TempRgnBufferSize;
}
*regionPtr++ = columnIndex;
--spaceLeftInBuffer;
}
columnIndex++;
inversion <<= 1;
} while (inversion);
// cap off the row with a row-capping marker.
if (!spaceLeftInBuffer)
{
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
regionPtr = rgnBuffer;
spaceLeftInBuffer = TempRgnBufferSize;
}
*regionPtr++ = 0x7FFF;
--spaceLeftInBuffer;
}
}
if (!spaceLeftInBuffer)
{
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
regionPtr = rgnBuffer;
spaceLeftInBuffer = TempRgnBufferSize;
}
*regionPtr++ = 0x7FFF; // cap off the rgn
// If the region is purely rectangular, minimize it and return it.
if (((*theRgn)->rgnSize == sizeof(Region)) &&
(((unsigned long)regionPtr - (unsigned long)rgnBuffer) == 18) &&
(rgnBuffer[1] == rgnBuffer[5]) &&
(rgnBuffer[2] == rgnBuffer[6]))
{
SetHandleSize((Handle)theRgn, sizeof(Region));
return;
}
// Flush the remainder of the buffer to the region.
if (!AddBufferToRgn(theRgn, rgnBuffer, regionPtr))
goto error;
return;
error:
(*theRgn)->rgnSize = sizeof(Region);
}
//-------------------------------------------------------------------------------------
// Search utilities.
//-------------------------------------------------------------------------------------
// GetBestData finds the best available data that is appropriate for the destination device.
// Initially, theBlock->dataDepth is set equal to the screen depth (by SetUpParamBlock). On
// exit, theData is set to the best available color data, theClut is set to the appropriate
// CLUT to use, and dataDepth is updated to reflect the depth of the output data. If no color
// data is available or appropriate, theData and theClut are set NULL and dataDepth is set to 1,
// all of which means that this is a one-bit plot. IconGetClut calls CheckClut to see if the clut
// of the specified depth fits into the current color environments. It returns false if it is not,
// causing us to degrade to a smaller bit-depth.
pascal void GetBestData(ParamBlock *theBlock)
{
long theData = theBlock->userData,
IconGetClut(ParamBlock *, short);
short checkDepth = 8,
whichIcon = theBlock->theGroup + largeIcon8; // whichIcon is initialized to the code
// for the deepest data in our size group
long IconGetClut(ParamBlock *, short);
IconRetriever theProc = theBlock->userProc;
void MakeClut(ParamBlock *);
do {
if ((theBlock->dataDepth >= checkDepth)
&& IconGetClut(theBlock, checkDepth)
&& (theBlock->theData = (*theProc)(theData, whichIcon)))
{
theBlock->dataDepth = checkDepth;
// if the the clut returned by IconGetClut is purged, build a new one
if (!(*(theBlock->theClut)))
MakeClut(theBlock);
return;
}
checkDepth -= 4;
--whichIcon;
} while (checkDepth);
theBlock->dataDepth = 1;
theBlock->theClut = NULL;
}
// ChooseBestSize maps a destination rectangle size to an icon size category.
short ChooseBestSize(const Rect *theRect)
{
int width = theRect->right - theRect->left,
height = theRect->bottom - theRect->top;
if ((width >= 32) || (height >= 32))
return largeIcon1;
if (height > 12)
return smallIcon1;
return miniIcon1;
}
#define FIXED(a) (long)(((a)<<16))
#define THEPIXMAP ((PixMapPtr)theBlock) // only works if the pixmap is the first element
// SetUpPixMap initializes the necessary fields of dataMap for a color plot.
pascal void SetUpPixMap(ParamBlock *theBlock)
{
unsigned short depth = theBlock->dataDepth;
THEPIXMAP->baseAddr = *(theBlock->theData);
// we use the pixelSize field to indicate whether or not we already have the dataMap
// initialized for a color plot, and if so to what depth.
if (THEPIXMAP->pixelSize == depth)
return;
THEPIXMAP->pmVersion = 0;
THEPIXMAP->packType = 0;
THEPIXMAP->packSize = 0;
THEPIXMAP->hRes = FIXED(72);
THEPIXMAP->vRes = FIXED(72);
THEPIXMAP->pixelType = 0;
THEPIXMAP->cmpCount = 1;
THEPIXMAP->planeBytes = 0;
THEPIXMAP->pixelSize = depth;
THEPIXMAP->cmpSize = depth;
// if the icon is a true CIcon, the rowBytes could be any arbitrary value. Otherwise,
// we can calculate the proper PixMap rowBytes value from the basic rowBytes and the
// depth.
if (theBlock->theGroup != aCIcon)
THEPIXMAP->rowBytes = ShortMul((unsigned short)(theBlock->rowBytes), depth) | 0x8000;
else
THEPIXMAP->rowBytes = ((CIconPtr)(theBlock->userProc))->iconPMap.rowBytes;
THEPIXMAP->pmTable = theBlock->theClut;
}
//-------------------------------------------------------------------------------------
// IconSuite Manipulation routines.
//-------------------------------------------------------------------------------------
// __NewIconSuite is called directly by the dispatcher to implement the NewIconSuite call.
pascal OSErr __NewIconSuite(IconSuiteHandle *theSuite)
{
*theSuite = (IconSuiteHandle)NewHandleClear(sizeof(IconSuite));
return MemErr;
}
// __MakeIconCache is called directly by the dispatcher to implement the MakeIconCache call.
pascal OSErr __MakeIconCache(IconCacheHandle *theHandle,
IconGetter makeIcon,
void *yourDataPtr)
{
IconCacheHandle theCache;
if (!(theCache = (IconCacheHandle)NewHandleClear(sizeof(IconCache))))
return MemErr;
(*theCache)->theSuite.type = CACHE; // set the suite type to indicate that this is a cache
(*theCache)->userPtr = yourDataPtr; // install the call-back and the data.
(*theCache)->userMethod = makeIcon;
*theHandle = theCache;
return noErr;
}
// EditIconSuite is called by __AddIconToSuite and __GetIconFromSuite (defined in IconUtils.a)
// If isAdd is false, EditIconSuite assumes refCon is a pointer to a Handle and returns the
// entry of type theType from theSuite (possibly NULL). If isAdd is true, the value in refcon
// is added to theSuite in the slot for data of type theType.
pascal OSErr EditIconSuite(long refCon, // Handle or referenct to handle of icon data
IconSuiteHandle theSuite,
ResType theType,
long isAdd)
{
Handle *theSlot = (*theSuite)->table;
long theEnd = (long)(theSlot + IconSpace);
ResType *theTable = typeTable;
do
{
if (theType == *theTable++)
{
if (isAdd)
*theSlot = (Handle)refCon;
else
*((Handle *)refCon) = *theSlot;
return noErr;
}
} while ((long)(++theSlot) < theEnd);
return paramErr;
}
// __ForEachIconDo is called by the trap dispatcher to implement the ForEachIconDo call. The action
// call-back is called for every member of theSuite specified by selector (See ERS for selector values).
pascal OSErr __ForEachIconDo(IconSuiteHandle theSuite,
unsigned long selector,
IconAction action,
void *yourDataPtr)
{
Handle *theTable = (Handle *)((*theSuite)->table);
long end = (long)(theTable + IconSpace); // there are more data registers than address
ResType *theTypes = typeTable;
OSErr rval = noErr;
char theState = HGetState((Handle)theSuite);
HLock((Handle) theSuite);
do
{
short i = IconSizeSpace;
do
{
if (selector & 0x00000001)
if (rval = (*action)(*theTypes, theTable, yourDataPtr))
goto out;
theTable++;
theTypes++;
selector >>= 1;
} while (--i);
selector >>= (8 - IconSizeSpace);
} while (theTable < (Handle *)end);
out:
HSetState((Handle) theSuite, theState);
return rval;
}
// This is the call back used with ForEachIconDo to implement the __GetIconSuite
// call below. A more sophisticated call-back could be used to implement a
// Get1IconSuite or to ensure that GetIconSuite only got icons from a single
// file. A user could do this kind of thing by rewriting GetIconSuite with
// their own call-back.
pascal OSErr GetIconFromResource(ResType theType, Handle *theIcon, void *yourDataPtr)
{
*theIcon = GetResource(theType, (short)yourDataPtr);
return noErr;
}
// Implements the GetIconSuite call. The selector allows only specific members of the
// suite to be asked for.
pascal OSErr __GetIconSuite(Handle *theHandle, short theID, unsigned long selector)
{
if (__NewIconSuite((IconSuiteHandle *)theHandle))
return MemErr;
__ForEachIconDo((IconSuiteHandle)(*theHandle), selector, GetIconFromResource, (void *)theID);
return noErr;
}
// Implements the SetSuiteLabel call. This specifies a default label for a suite
// which is used if no label is specified by the transform. Yes, this is currently used.
// Namely, by the process manager for the Apple Menu.
pascal OSErr __SetSuiteLabel(IconSuiteHandle theSuite, unsigned short theLabel)
{
if (theLabel > 7)
return paramErr;
theLabel <<= labelShift;
(*theSuite)->label = theLabel;
return noErr;
}
// Implements the GetSuiteLabel. Just for completeness.
pascal short __GetSuiteLabel(IconSuiteHandle theSuite)
{
return (((*theSuite)->label) >> labelShift);
}
// This is the call back used with ForEachIconDo to implement DisposIconSuite. An icon
// is only disposed if it is not a resource.
pascal OSErr Disposer(ResType , Handle *theIcon, void *)
{
if (*theIcon && (HomeResFile(*theIcon) == -1))
DisposHandle(*theIcon);
*theIcon = NULL;
return noErr;
}
// Implements the DisposeIconSuite call. disposeData indicates whether an attempt should
// be made to dispose of the data in the suite. Only non-resource handles will be disposed.
pascal OSErr __DisposeIconSuite(IconSuiteHandle theSuite, Boolean disposeData)
{
if (disposeData)
(void)__ForEachIconDo(theSuite, svAllAvailableData, Disposer, NULL);
DisposHandle((Handle)theSuite);
return MemErr;
}
// These are the call-backs used with all the generic routines (PlotIconGeneric, HitTestIconGeneric and
// IconToRgnGeneric) to implement PlotIconID, PlotIconMethod, PlotIconSuite, HitTestIconID, etc. etc.
// FromMethod and FromResource are defined in IconUtils.a
Handle FromMethod(long data, IconType theIcon);
Handle FromResource(long data, IconType theIcon);
Handle FromSuite(long data, IconType theIcon)
{
IconCachePtr theCache = *((IconCacheHandle)data);
Handle rval = theCache->theSuite.table[theIcon];
// if a suite is a cache (See Cache routines below) and we don't have any data, we call the user's
// call back to get some data.
if (!rval && theCache->theSuite.type)
{
IconGetter theProc = theCache->userMethod;
if (theProc)
{
rval = (*theProc)(typeTable[theIcon], theCache->userPtr);
(*((IconSuiteHandle)data))->table[theIcon] = rval;
}
}
return rval;
}
// AdjustRgn checks the from and to rects and decides whether the region needs mapping,
// offsetting or nothing. It is defined in IconUtils.a and is called by DoRegionTransform
// and HitTestIconGeneric.
extern pascal void AdjustRgn(RgnHandle maskRgn, Rect *from, Rect *to);
// DoRegionTransform performs a transform using region operations. This will be called
// when the useRegions flag is true.
pascal void DoRegionTransform(ParamBlock *theBlock, short theTransform)
{
RgnHandle patRgn;
Boolean isASuite = (theBlock->theGroup != aCIcon);
PenState saveState;
char maskState;
GetPenState(&saveState);
// If we are not a true CIcon, we need to generate a region from the icon mask. The
// locking of theMask is redundant in the color case and extraneous in the case where
// we synthesize a mask but the complexity of determining exactly whether it needs to be done
// dosen't buy that much.
if (isASuite)
{
maskState = HGetState(theBlock->theMask);
HLock(theBlock->theMask);
patRgn = NewRgn();
MaskRgn(patRgn, theBlock);
AdjustRgn(patRgn, &(theBlock->maskMap.bounds), &(theBlock->theRect));
}
else
patRgn = (RgnHandle)theBlock->userData; // This is used for true CIcon to hold a region equivalent
// to its mask (See SetupParamBlockForCIcon)
switch (theTransform)
{
// makes an outline and fills it with a 25% gray pattern
case ttOpen:
FillRgn(patRgn, GetBlack());
InsetRgn(patRgn, 1, 1);
FillRgn(patRgn, GetLightGray());
break;
// Or's over the data (already drawn) with a 25% gray pattern
case ttOffline:
PenPat(GetLightGray());
PenMode(patOr);
PaintRgn(patRgn);
break;
// Bit-clears the already drawn data with a 50% pattern
case ttDisabled:
PenPat(GetHalfGray());
PenMode(patBic);
PaintRgn(patRgn);
break;
}
SetPenState(&saveState);
if (isASuite)
{
DisposeRgn(patRgn);
HSetState(theBlock->theMask, maskState);
}
}
// The following routines are used by DoBitMapTransform to accomplish the various
// transform effects. They rely on the fact that the theBlock->maskMap describes
// exactly the countours of the shape they are transforming. This is guaranteed
// by PrescaleMask (See below)
// DoOutline takes the pre-scaled maskMap and OR's an outline of the icon
// over destination. It is used to implement the open transform.
void DoOutline(BitMapPtr maskMap, unsigned long *destination)
{
unsigned char *source = (unsigned char *)(maskMap->baseAddr);
unsigned long dataMask = (unsigned long)(-1),
lastRow,
row,
nextRow,
destRow,
checkRow,
setMask;
short end = maskMap->bounds.bottom - 1,
i;
// data mask is used to mask off all but the bits belonging to one row of the mask map.
dataMask <<= (32 - maskMap->bounds.right);
// last row represents the row of mask data above the one we're analyzing. It is initialized
// to zero because logicly, there are zero bits outside the bounds of our maskMap.
lastRow = 0L;
// row is the row of mask data we are currently trying to hollow out.
row = *((unsigned long *)source) & dataMask;
source += maskMap->rowBytes;
// we march through the rows of our mask, hollowing our rows as we go. We only go as far
// as the row before the bottom-most row, since we are only interested in examining rows
// that are samwiched between other rows with data.
for (i = 0; i < end; i++)
{
// destRow will be our hollowed out row. It is initialized to row since we don't know if it
// needs hollowing.
destRow = row;
// nextRow is the row below the one we are examining. We advance our source pointer here. We
// won't be advancing off the icon because we're stopping one row before the last.
nextRow = *((unsigned long *)source) & dataMask;
source += maskMap->rowBytes;
// if row has data above and below it, we examine its bits left-to-right. If any one bit has
// a one-bit to left of it, to the right of it, on top of it and below it, we turn that bit
// off in destRow. It works, really.
if (destRow)
{
if (lastRow && nextRow)
{
setMask = 0x40000000;
for (checkRow = row; checkRow; checkRow <<= 1, setMask >>= 1)
if (((checkRow & 0xE0000000) == 0xE0000000) && (setMask & lastRow) && (setMask & nextRow))
destRow ^= setMask;
}
// here we OR the destRow with the destination data
*destination |= destRow;
}
// advance the destination pointer as well as lastRow and row.
++destination;
lastRow = row;
row = nextRow;
}
// nextRow now contains the data from the last row in the icon. It either has data, in which case
// it gets OR'd in its entirety, or it dosen't and we do nothing.
if (nextRow)
*destination |= nextRow;
}
// ApplyPattern is used to either bitClear the data in theMap with a 50% pattern (for the disabled transform)
// or to OR the data with a 25% pattern (for offline and open).
pascal void ApplyPattern(BitMapPtr theMap, BitMapPtr theMask, Boolean bitClear)
{
unsigned long *mapData = (unsigned long *)(theMap->baseAddr);
unsigned char *maskData = (unsigned char *)(theMask->baseAddr); // we know how wide this buffer is
short rowBytes = theMask->rowBytes,
end = theMask->bounds.bottom;
do {
if (bitClear)
{
*mapData++ &= 0xAAAAAAAA;
*mapData++ &= 0x55555555;
}
else
{
*mapData++ |= (0x88888888 & *((unsigned long *)maskData));
maskData += rowBytes;
*mapData++ |= (0x22222222 & *((unsigned long *)maskData));
maskData += rowBytes;
}
end -= 2;
} while (end);
}
// DoBitMapTransform causes an icon to appear transformed (open, offline or disabled). If onePass is true,
// It means that we are plotting a one-bit icon and we want to combine the transform patterns with the
// icon data off-screen and plot it all at once. If onePass is false, it means we are plotting a color
// icon and that the color data has already been plotted.
pascal void DoBitMapTransform(ParamBlock *theBlock, short theTransform, Boolean onePass)
{
BitMap transformMap; // BitMap used for transform data.
unsigned long transformBuffer[32], // offscreen area where we will create transform data
*buffPtr = transformBuffer,
*endPtr = buffPtr + 32;
// IsNotPreScaled is true if we did not have to scale the mask prior to this point. We need to know this to decide
// whether to scale the data for one pass attempt and whether we should use CopyMask.
Boolean IsNotPreScaled = (MakeLong(theBlock->maskMap.bounds.bottom) == MakeLong(theBlock->dataMap.bounds.bottom));
// empty out the transformBuffer, since we are OR'ing data over it.
do {
*buffPtr++ = 0L;
} while (buffPtr < endPtr);
// initialize transform BitMap
transformMap.baseAddr = (Ptr)transformBuffer;
transformMap.rowBytes = 4;
transformMap.bounds = theBlock->maskMap.bounds;
// if we are trying to do this plot in one pass, we copy the one-bit data into the transformBuffer. If the mask has
// not been prescaled, the image data data will not need scaling and we can just copy it into the buffer. (this will
// always be the case in the Finder since they prescale their old-style icons into different size categories) Otherwise,
// we will have to stretch the data into transform buffer;
if (onePass && (theTransform != ttOpen))
{
if (IsNotPreScaled)
{
short height = theBlock->dataMap.bounds.bottom;
unsigned char *theData = *(theBlock->theMask);
buffPtr = transformBuffer;
do {
*buffPtr++ = *((unsigned long *)theData);
theData += theBlock->rowBytes;
} while (--height);
}
else // use srcXor because we do not know the foreColor or backColor but we know the destination is all zeros
CopyBits((BitMapPtr)(&(theBlock->dataMap)), &transformMap, &(theBlock->dataMap.bounds), &(transformMap.bounds), srcXor, NULL);
}
// OR in an outline of the mask if the transform is ttOpen
if (theTransform == ttOpen)
DoOutline(&(theBlock->maskMap), transformBuffer);
// if the transform is ttDisabled, bit clear the data with a 50% gray. If it is ttOffline or ttOpen, OR the data with 25% gray
if (theTransform == ttDisabled)
ApplyPattern(&transformMap, &(theBlock->maskMap), true);
else
ApplyPattern(&transformMap, &(theBlock->maskMap), false);
// I'm putting this in a block to take advantage of register re-use
{
Rect *maskRect = &(theBlock->maskMap.bounds);
// if we're plotting a one-bit, transformed icon or if we're plotting an opened icon, we have all the
// data we need in the transform map and we'll try to render that in one shot (although we may have
// to put down the mask separately. See comments below). Otherwise, we have a color transform and
// we assume that the basic icon data has already been rendered and that we're just going to OR over it
// with our transform pattern.
if (onePass || (theTransform == ttOpen))
{
// The choice of which rendering technique to use is based on three things. If you have don't have color
// QuickDraw, CopyMask dosen't stretch bits so you generally can't use it. If you're printing or plotting
// to an open PICT, again CopyMask won't work. If you're mask is pre-scaled, and therefore has different
// bounds than your data, CopyMask won't work (a bug). In general you want to try to use CopyMask because
// it goes down in one-pass and avoids any possible flashing. If you can't use it, you BitClear the mask
// and OR over it with data, which works regardless of fore and backGround colors.
if (HaveCQD() && (theBlock->onScreen) && IsNotPreScaled)
CopyMask(&transformMap, &(theBlock->maskMap), theBlock->dstMap, maskRect, maskRect, &(theBlock->theRect));
else
{
CopyBits(&(theBlock->maskMap), theBlock->dstMap, maskRect, &(theBlock->theRect), srcBic, NULL);
CopyBits(&transformMap, theBlock->dstMap, maskRect, &(theBlock->theRect), srcOr, NULL);
}
}
else
CopyBits(&transformMap, theBlock->dstMap, maskRect, &(theBlock->theRect), srcOr, NULL);
}
}
// Render is called by PlotDeep, PlotShallowBlackAndWhite and PlotShallowColor. It uses the same criterion described above
// to decide which rendering technique to use with the main icon image data.
pascal void Render(ParamBlock *theBlock)
{
Rect *destRect = &(theBlock->theRect);
if (HaveCQD() && (theBlock->onScreen) && (MakeLong(theBlock->dataMap.bounds.bottom) == MakeLong(theBlock->maskMap.bounds.bottom)))
CopyMask((BitMapPtr)(&(theBlock->dataMap)), &(theBlock->maskMap), theBlock->dstMap,
&(theBlock->dataMap.bounds), &(theBlock->maskMap.bounds), destRect);
else
{
CopyBits(&(theBlock->maskMap), theBlock->dstMap, &(theBlock->maskMap.bounds), destRect, srcBic, NULL);
CopyBits((BitMapPtr)(&(theBlock->dataMap)), theBlock->dstMap, &(theBlock->dataMap.bounds), destRect, srcOr, NULL);
}
}
// PlotDeep, PlotShallowBlackAndWhite and PlotShallowColor are all called by PlotTheIcon, once it has been decided what kind of data
// we will be plotting. When these routines are called, theBlock has theDevice, theClut and theData set up. The clipRgn has also been
// set up to whatever extent is necessary.
// PlotDeep is used to plot color data to a deep color screen.
pascal void PlotDeep(ParamBlock *theBlock)
{
ColorSpec foreGround,
backGround;
Handle theData = theBlock->theData;
short theTransform = theBlock->theTransform;
char dataState;
// the CLUT is purgeable and rendering may move memory.
HNoPurge((Handle)(theBlock->theClut));
// if we've gotten as far as calling PlotDeep we know that there's a good, disabled clut for this screen.
// Setting the local copy theTransform to none indicates that we should not attempt a bit cleared disabled
// transform.
if (theTransform == ttDisabled)
theTransform = ttNone;
// if our data is unloaded, we attempt to load it.
if (!(*theData))
LoadResource(theData);
// lock down the image data because CopyBits or CopyMask may move memory (if only rarely)
dataState = HGetState(theData);
HLock(theData);
SetUpPixMap(theBlock); // initialize dataMap with additional fields needed for a PixMap
// Save the color environment
SaveFore(&foreGround);
SaveBack(&backGround);
// Ensure black and white for fore and backGround color
ForeColor(blackColor);
BackColor(whiteColor);
// theTransform is ttOpen, the transform code will do all the work. Otherwise, we render
// the main color image.
if (theTransform != ttOpen)
Render(theBlock);
// If there's a transform, we OR or bit clear over the image in an appropriate way.
if (theTransform)
{
if (theBlock->useRegions)
DoRegionTransform(theBlock, theTransform);
else
DoBitMapTransform(theBlock, theTransform, false);
}
// restore the color environment
RestoreFore(&foreGround);
RestoreBack(&backGround);
HPurge((Handle)(theBlock->dataMap.pmTable));
HSetState(theData, dataState);
}
// PlotShallowBlackAndWhite is used to plot to a one-bit screen (Either a black and white Mac or
// a monitor set to one-bit)
pascal void PlotShallowBlackAndWhite(ParamBlock *theBlock)
{
short theTransform = theBlock->theTransform;
ColorSpec foreGround,
backGround;
theBlock->dataMap.baseAddr = *(theBlock->theMask);
// If the icon is selected, we invert the current fore and backGround color
if (theBlock->selected)
{
if (HaveCQD())
{
RGBColor RGBFore,
RGBBack;
SaveFore(&foreGround);
SaveBack(&backGround);
GetForeColor(&RGBFore);
GetBackColor(&RGBBack);
RGBForeColor(&RGBBack);
RGBBackColor(&RGBFore);
}
else
{
GrafPtr curPort = myGetPort();
*((long *)(&foreGround)) = curPort->fgColor;
*((long *)(&backGround)) = curPort->bkColor;
ForeColor(*((long *)(&backGround)));
BackColor(*((long *)(&foreGround)));
}
}
// If we have a transform, use the transform code to render the icon and the transform. Otherwise,
// just render the data.
if (theTransform)
{
// If we have to use regions to generate the transformed image, render the icon and modify it
// by the transform. Otherwise, do the operation in one pass with DoBitMapTransform.
if (theBlock->useRegions)
{
if (theTransform != ttOpen)
Render(theBlock);
DoRegionTransform(theBlock, theTransform);
}
else
DoBitMapTransform(theBlock, theTransform, true);
}
else
Render(theBlock);
// If we mucked with the color environment, restore it.
if (theBlock->selected)
{
if (HaveCQD())
{
RestoreFore(&foreGround);
RestoreBack(&backGround);
}
else
{
ForeColor(*((long *)(&foreGround)));
BackColor(*((long *)(&backGround)));
}
}
}
// PlotShallowColor is for plotting one-bit data to a deep screen
pascal void PlotShallowColor(ParamBlock *theBlock)
{
RGBColor foreColor,
backColor;
ColorSpec foreGround,
backGround;
short screenDepth = theBlock->screenDepth,
theTransform = theBlock->theTransform,
theLabel;
Boolean isDeep = (screenDepth > 4); // Are we deeper than 4-bits?
// this check makes sure that labeling does not happen in gray-scale or in 2-bit mode
if ((screenDepth <= 2) || (theBlock->theDevice && !((*(theBlock->theDevice))->gdFlags & 0x1)))
theLabel = 0;
else
theLabel = theBlock->theLabel;
if (theLabel)
(void)GetLabel(theLabel, &foreColor, NULL);
else
GetForeColor(&foreColor);
if (theTransform == ttOpen)
{
backColor.red = 52266; // It seems strange to hard-code this color.
backColor.green = 52266; // Should it maybe be function of the label
backColor.blue = 65322; // or some other factor? HMMMM?
}
else
GetBackColor(&backColor);
theBlock->dataMap.baseAddr = *(theBlock->theMask);
// This check allows for a pure disabled color to be used on a screen that has one.
// Otherwise, a disabled icon will be Bit-Cleared with a 50% pattern. GetGray has
// the side-effect of setting foreColor to the average of backColor and foreColor.
// It returns false if no good average could be found. The condition should read
// something like "If we're on a an 8-bit monitor or we're on a 4-bit monitor and
// there's no label, try to find a disabled color for this icon. If you can find
// one, use that color as the foreground color and treat the icon as not transformed."
if ((theTransform == ttDisabled) && (isDeep || (!theLabel)) &&
GetGray(theBlock->theDevice, &backColor, &foreColor))
theTransform = ttNone;
// If the icon is selected, set up the color environment to make it appear darkened.
if (theBlock->selected)
{
void Darken(RGBColor *aColor);
// on a four-bit screen, there is almost never an appropriate, darkened color available,
// so we don't try.
if (isDeep)
Darken(&foreColor);
Darken(&backColor);
}
SaveFore(&foreGround);
SaveBack(&backGround);
// Note: There is no guarantee that a good approximation of foreColor and backColor exist
// on the destination device. This is a hole in the whole color protection scheme in this code.
// These colors should probably be validated in some way before they are used, falling back on a
// black & white image.
RGBForeColor(&foreColor);
RGBBackColor(&backColor);
// Use the same criterion as in PlotShallowBlackAndWhite for render the image.
if (theTransform)
{
if (theBlock->useRegions)
{
if (theTransform != ttOpen)
Render(theBlock);
DoRegionTransform(theBlock, theTransform);
}
else
DoBitMapTransform(theBlock, theTransform, true);
}
else
Render(theBlock);
RestoreFore(&foreGround);
RestoreBack(&backGround);
}
// This routine simply decides which of the two PlotShallow flavors to use. It exists for
// historical reasons and should be folded into PlotTheIcon in a future version.
pascal void PlotShallow(ParamBlock *theBlock)
{
theBlock->dataMap.rowBytes = theBlock->rowBytes;
theBlock->dataMap.pixelSize = 1;
if (theBlock->screenDepth >= 2)
PlotShallowColor(theBlock);
else
PlotShallowBlackAndWhite(theBlock);
}
// PlotTheIcon sets up the ParamBlock for plotting the icon to the particular screen and sets up
// the clip region as appropriate. It is either called directly by PlotIconGeneric or indirectly
// by way of DeviceLoop. On entry, theBlock has been set up by SetupParamBlock, MakeBoundary has
// been called, alignment has been done and the mask has been prescaled if necessary. PlotTheIcon
// gets the best available data for the screen depth and calls one of the plotting routines above
// to render the data with its transform.
pascal void PlotTheIcon(short screenDepth,
short flags,
GDHandle theDevice,
ParamBlock *theBlock)
{
Rect boundRect,
sectRect;
RgnHandle saveClip = NULL;
short theTransform = theBlock->theTransform;
// flags will be non-zero if PlotTheIcon was called from DeviceLoop. Flags will be zero if it
// was called directly from PlotIconGeneric which implies a single target device.
if (theDevice && flags)
{
// the following code generates a global rectangle which bounds the icon in its destination
// size.
boundRect = theBlock->boundary;
MapRect(&boundRect, &(theBlock->dataMap.bounds), &(theBlock->theRect));
LocalToGlobal((Point *)&boundRect);
LocalToGlobal((Point *)(&(boundRect.bottom)));
// If this global rectangle dosen't intersect the GDevice were checking, then we won't plot to it.
// Otherwise, we will check if it is only partially on the screen. If it is, we will adjust the
// clipRgn so that only the portion of the icon that appears on the screen will be drawn.
if (SectRect(&((*theDevice)->gdRect), &boundRect, &sectRect))
{
if ((!EqualRect(&sectRect, &boundRect)) && (!theBlock->dontDraw))
{
RgnHandle realClip = myGetPort()->clipRgn;
saveClip = NewRgn();
GetClip(saveClip);
GlobalToLocal((Point *)(&sectRect));
GlobalToLocal((Point *)(&(sectRect.bottom)));
ClipRect(&sectRect);
SectRgn(saveClip, realClip, realClip);
}
}
else
return;
}
theBlock->screenDepth = screenDepth;
theBlock->theDevice = theDevice;
// The following conditional reads - "try to plot the icon as color if A) we are plotting to an 8-bit
// screen and the transform is not ttOpen or B) we are plotting to a 4-bit screen, the icon is not
// selected, and the transform is neither ttOpen nor ttDisabled. Otherwise, treat it as one-bit"
if (((screenDepth > 4) && (theTransform != ttOpen)) ||
((screenDepth == 4) && (!(theBlock->selected)) && (!(theTransform & ttDisabled))))
{
theBlock->dataDepth = screenDepth; // Initialize data depth to screen depth. GetBestData may
GetBestData(theBlock); // change this value.
}
else
{
theBlock->dataDepth = 1;
theBlock->theClut = NULL;
}
// PlotTheIcon is being called from LoadIconCache, we're done loading the data and we run away.
if (theBlock->dontDraw)
return;
if (theBlock->dataDepth >= 4)
PlotDeep(theBlock);
else
PlotShallow(theBlock);
if (saveClip)
{
SetClip(saveClip);
DisposeRgn(saveClip);
}
}
// MakeBoundary creates a rectangle within the bounds of the maskMap (before it is pre-scaled) which
// bounds the non-zero bits of the mask. It returns false if the mask is all zeros.
Boolean MakeBoundary(ParamBlock *theBlock)
{
unsigned char *ptr = (unsigned char *)(theBlock->maskMap.baseAddr); // <57>
unsigned short mark,
side,
rowBytes = theBlock->maskMap.rowBytes,
height = theBlock->maskMap.bounds.bottom;
unsigned long temp,
checkSum,
sizeMask = (theBlock->rowBytes < 4) ? 0xFFFF0000 : 0xFFFFFFFF;
// Find the first row that has data and call it the top.
for (side = 0; side < height; ++side, ptr += rowBytes)
if (*((unsigned long *)ptr) & sizeMask)
break;
// If no data in the mask return false
if (side == height)
return false;
theBlock->boundary.top = side;
checkSum = 0;
// mark is set equal to the row number of the last row with data. checkSum
// is set to the OR of all the rows.
for (; side < height; ++side, ptr += rowBytes)
if (temp = (*((unsigned long *)ptr) & sizeMask))
{
mark = side;
checkSum |= temp;
}
// the bottom is equal to the row number of the last row with data plus one.
theBlock->boundary.bottom = mark + 1;
// checkSum is shifted left until the most significant bit is one.
for (side = 0; !(checkSum & 0x80000000); checkSum <<= 1, ++side);
// the number of shifts will be the left boundary
theBlock->boundary.left = side;
// checkSum is further shifted left until all the data is gone.
for (; checkSum; checkSum <<= 1, ++side);
// the number of shifts plus the left offset equals the right boundary.
theBlock->boundary.right = side;
return true;
}
// CreateMask generates a mask from some data. It is generated into a buffer
// at the end of theBlock. CalcMask is used to generate the data.
void CreateMask(ParamBlock *theBlock, long maskByteLength)
{
Ptr maskBuffer = (Ptr)(theBlock->maskBuffer);
short rowBytes = theBlock->maskMap.rowBytes,
height = ShortDiv(maskByteLength, rowBytes);
// This catches the odd case in which an icon is larger than an icon
// but smaller than an icon with a mask. This makes sure we only use
// 32 bits of height so as not to overrun our buffer.
if (height > 32)
height = 32;
CalcMask((Ptr)(*(theBlock->theMask)),
maskBuffer, rowBytes, 4,
height, rowBytes >> 1);
theBlock->maskMap.baseAddr = maskBuffer;
theBlock->maskMap.rowBytes = 4;
}
// SetupParamBlock finds the appropriate size category of icon to be dealing
// with, gets (or synthesizes) the mask for the data, locks all necessary data
// down, sets up the maskMap and some parameters that don't change throughout
// the course of the plot (or make region) call. It returns FALSE if no mask
// (1-bit data) could be found in any size category.
short SetupParamBlock(ParamBlock *theBlock)
{
short size,
rowBytes,
desired = ChooseBestSize(&(theBlock->theRect));
long maskByteLength;
Handle theMask;
// Find a mask of the right size category
for (size = desired; size <= miniIcon1; size += IconDepthSpace)
if (theMask = (*(theBlock->userProc))(theBlock->userData, size))
goto done;
for (size = desired - IconDepthSpace; size >= largeIcon1; size -= IconDepthSpace)
if (theMask = (*(theBlock->userProc))(theBlock->userData, size))
goto done;
return false;
done:
theBlock->theMask = theMask;
theBlock->theGroup = size;
theBlock->theData = NULL;
// size here means the basic dimension (height) of the icon of specific size
// category. rowBytes here is a basic rowBytes of the one bit data.
if (size == largeIcon1)
{
rowBytes = 4;
size = 32;
}
else
{
rowBytes = 2;
if (size == smallIcon1)
size = 16;
else
size = 12;
}
// the basic rowBytes is stored because maskMap.rowBytes might be changed to 4
// arbitrarily if the mask is synthesized.
theBlock->rowBytes = rowBytes;
// maskMap is setup
theBlock->maskMap.rowBytes = rowBytes;
MakeLong(theBlock->maskMap.bounds) = 0L;
theBlock->maskMap.bounds.bottom = size;
theBlock->maskMap.bounds.right = rowBytes << 3; // this insures that a mini icon is 12X16
// lock down the mask member and remember its state.
if (!(*theMask))
LoadResource(theMask); // theoretically its cool to let LoadResource decide whether the resource is loaded or not
// but LoadResource has been back-patched thrice at the time of this writing and all kinds
// of annoying stuff goes before the simple check of the master pointer. sigh.
// The following does a size check to guess whether an icon has a mask or not. If not,
// a probable mask is synthesized.
rowBytes *= size; // I'm hijacking rowbytes to use as a temp
rowBytes <<= 1;
maskByteLength = myGetHandleSize(theMask);
// if there is no mask data appended to the one-bit version of this icon (i.e. its not a proper ICN#, ics#, icm#)
// synthesize a mask. Otherwise, we just point the maskMap baseAddr at the mask data in the one-bit icon.
if (maskByteLength < rowBytes)
{
rowBytes >>= 1;
CreateMask(theBlock, maskByteLength);
}
else
theBlock->maskMap.baseAddr = (*theMask) + (maskByteLength >> 1);
// This is just to allow the Finder to use some SICN's as minis without special-casing (Grrrr!)
if ((theBlock->theGroup == miniIcon1) && (maskByteLength > rowBytes))
{
long center = ((theBlock->theRect.bottom + theBlock->theRect.top) >> 1);
theBlock->theRect.top = center - 8;
theBlock->theRect.bottom = center + 8;
theBlock->maskMap.bounds.bottom = 16;
}
theBlock->dataMap.bounds = theBlock->maskMap.bounds; // the mask and data have the same source bounds (may change later)
theBlock->dataMap.pixelSize = 0; // functions as flag saying "the data map is not yet initialized as a color grafport"
theBlock->useRegions = false; // Initially assume that we will not use regions for transforms and hit testing.
// PrescaleMask may change this value.
return true;
}
// PrescaleMask is used after SetupParamBlock. If the destination rectangle of an icon is
// less than or equal to 32 in both dimensions, and if it is different in height or width
// to maskMap.bounds, we do the stretching that will take place off-screen. The result ends
// up in the maskBuffer and maskMap is modified to point to it. After calling PrescaleMask,
// you are guaranteed that the maskMap describes the countours of the icon's mask as it will
// be drawn (which is important for doing transforms and for hit-testing without doing region
// operations). If an icon is larger than our off-screen buffer (never under 7.0), we use
// region operations for hit-testing and transforms. PrescaleMask is called by HitTestIconGeneric
// and by PlotIconGeneric (only if there is a transform).
pascal void PrescaleMask(ParamBlock *theBlock)
{
short width = theBlock->theRect.right - theBlock->theRect.left,
height = theBlock->theRect.bottom - theBlock->theRect.top;
if ((width > 32) || (height > 32))
{
theBlock->useRegions = true;
return;
}
// This checks if any stretching will be done… we know that maskMap.bounds has (0,0) for its
// upper left coordinate.
if ((width != theBlock->maskMap.bounds.right) || (height != theBlock->maskMap.bounds.bottom))
{
unsigned long tempBuffer[32];
BitMap tempMap;
Rect newBounds;
MakeLong(newBounds) = 0;
newBounds.right = width;
newBounds.bottom = height;
MakeLong(tempMap.bounds) = 0;
MakeLong(tempMap.bounds.bottom) = 0x00200020;
tempMap.rowBytes = 4;
tempMap.baseAddr = (Ptr)theBlock->maskBuffer;
// if maskBuffer is currently being used, as in the case of a missing mask we have synthesized,
// we dump it temporarily to an intermediate buffer.
if (theBlock->maskMap.baseAddr == theBlock->maskBuffer)
{
theBlock->maskMap.baseAddr = (Ptr)tempBuffer;
BlockMove(theBlock->maskBuffer, tempBuffer, 32 * sizeof(unsigned long));
}
CopyBits(&(theBlock->maskMap), &tempMap, &(theBlock->maskMap.bounds), &newBounds, srcCopy, NULL);
MakeLong(theBlock->maskMap.bounds.bottom) = MakeLong(newBounds.bottom);
theBlock->maskMap.baseAddr = (Ptr)theBlock->maskBuffer;
theBlock->maskMap.rowBytes = 4;
}
}
// This is the routine that all plotting goes through. By the time we get to PlotIconGeneric, we
// have a handler routine and some data pushed that identifies how we are going to get suite
// members. For example, in the case of PlotIconID, the refCon is equal to the resource ID and
// the IconRetriever is set to a function call FromResource which calls GetResource for an icon
// type and the given ID. The dontDraw parameter allows for LoadIconCache to act as if it were
// drawing, but in stead just load the data.
//
// PlotIconGeneric is called from assembly language wrappers in IconUtils.a that Implement
// PlotIconID, PlotIconSuite, PlotIconMethod and LoadIconCache.
#define PrintVars (*((short *)0x948))
pascal OSErr PlotIconGeneric(const Rect *theRect,
short align,
short transform,
long refCon,
IconRetriever iconProc,
short dontDraw)
{
GrafPort *curPort;
ParamBlock theBlock;
// if this is a call to LoadIconCache and the input is not an iconcache,
// exit with a paramErr
if (dontDraw && !((*((IconSuiteHandle)refCon))->type))
return paramErr;
// save some basic information in the parameter block
theBlock.theRect = *theRect;
theBlock.userData = refCon;
theBlock.userProc = iconProc;
theBlock.theTransform = transform & transformMask;
theBlock.selected = transform & ttSelected;
theBlock.dontDraw = dontDraw;
curPort = myGetPort();
// onScreen is true if a print page is not open and no picture is open
theBlock.onScreen = ((PrintVars == -1) && (!(curPort->picSave)));
theBlock.dstMap = &(curPort->portBits);
if (!SetupParamBlock(&theBlock))
return noMaskFoundErr;
// See MakeBoundary above. If no data was found in the mask portion of the
// one-bit data, then clearly CopyMask would not plot anything. So why bother
// doing any more work? This is not the same case as no one-bit data available.
if (!MakeBoundary(&theBlock))
goto out;
// boundary-making and alignment have to take place before PrescaleMask, because they
// make certain assumptions about the mask which in general are no longer valid after the
// pre-scale.
if (align)
PerformAlignment(&theBlock, align);
// this line performs an off-screen stretching of the mask data if there is a
// a difference between the size of the destination rect and the mask size. Thus,
// the maskMap field will always have a correctly scaled mask of the data which
// can be used for creating transform effects etc. The one exception would be if the
// destination rect was so large that the pre-scaled mask would not fit in the 32 X 32
// buffer in the parameter block. In this case, the useRegions flag would be set and
// regions would be used for transforms, hit-testing etc.
if (theBlock.theTransform)
PrescaleMask(&theBlock);
// If we are plotting to a PixMap, we lock down the mask (because color CopyBits/CopyMask
// can move memory), get label information and decide how to plot based on our screen setup.
if (curPort->portBits.rowBytes < 0)
{
Handle theMask = theBlock.theMask;
char maskState = HGetState(theMask);
HLock(theMask);
theBlock.theLabel = (transform & labelMask) >> labelShift;
// If we are plotting to actual video devices, then we need to see if we've got a
// device set by SetIconDevice or if our system has only one screen. If so, we call
// PlotTheIcon once with the appropriate device. If not, we have multiple monitors and
// we need to use DeviceLoop to call PlotTheIcon in the appropriate way. If we're not
// plotting to a video device, but rather to an offscreen thing or a printer, we call PlotTheIcon
// once with a device of NULL, meaning that we're plotting off-screen.
if (theBlock.onScreen)
{
GlobalIconDataPtr theGlobals = GetGlobalPtr();
GDHandle theScreen;
if (theGlobals->deviceListSize == 1)
theScreen = theGlobals->deviceCheckList[0].theDevice;
else
theScreen = theGlobals->virtualScreen;
if (theScreen)
PlotTheIcon((*((*theScreen)->gdPMap))->pixelSize, 0, theScreen, &theBlock);
else
DeviceLoop(NULL, (DeviceLoopDrawingProcPtr)PlotTheIcon, (long)(&theBlock),
dontMatchSeeds | singleDevices | allDevices);
}
else
PlotTheIcon(8, 0, NULL, &theBlock);
HSetState(theMask, maskState);
}
else
{
theBlock.theLabel = 0; // If we're not plotting to a CGrafPort, we don't use a label.
PlotTheIcon(1, 0, NULL, &theBlock);
}
out:
return noErr;
}
// IconToRgnGeneric is called by a bunch of assembly language wrappers in IconUtils.a to implement
// IconIDToRgn, IconSuiteToRgn and IconMethodToRgn. It is also called from HitTestIconGeneric if regions
// will be used to perform hit-testing. It takes a valid region, the rect to which the theoretical icon was drawn, the
// alignment used and the same kind of call-back used by PlotIconGeneric above. theRgn is changed to be
// equivalent to the icon's mask when aligned and stretched to the destination rect.
pascal OSErr IconToRgnGeneric(RgnHandle theRgn,
Rect *theRect,
IconAlignmentType align,
long refCon,
IconRetriever iconProc)
{
ParamBlock theBlock;
// minimal set up for call to SetupParamBlock (I know, its a little squirly that SetupParamBlock needs set up but…)
theBlock.theRect = *theRect;
theBlock.userData = refCon;
theBlock.userProc = iconProc;
// we set up the ParamBlock, which finds the right-sized mask for the destination rect and loads it. It also
// may synthesize a mask if a SICN or ICON is used for a one-bit icon.
if (!SetupParamBlock(&theBlock))
return noMaskFoundErr;
// if there is no data in the mask, we make an empty region. Makes sense.
if (MakeBoundary(&theBlock))
{
// MaskRgn moves memory so we lock down our mask. Note for future change: a check should be made here
// to see if the mask is in maskBuffer, in which case this lock and restore is extraneous.
Handle theMask = theBlock.theMask;
char maskState = HGetState(theMask);
HLock(theMask);
// If there's an alignment specified, we do it.
if (align)
PerformAlignment(&theBlock, align);
// make the region and stretch it appropriately
MaskRgn(theRgn, &theBlock);
AdjustRgn(theRgn, &(theBlock.maskMap.bounds), &(theBlock.theRect));
HSetState(theMask, maskState);
}
else
SetEmptyRgn(theRgn);
return noErr;
}
// HitTestIconMaskGeneric is called from HitTestIconGeneric (See Below) if regions are not used for the
// hit-testing. It takes the same input as HitTestIconGeneric. The test is performed by stretching the
// mask data off-screen (if necessary) and comparing the point or rect to this data arithmeticly.
#define isAPoint 0
#define isARect 1
pascal Boolean HitTestIconMaskGeneric(unsigned long pointOrRect,
short IsRect,
Rect *theRect,
IconAlignmentType align,
long refCon,
IconRetriever iconProc)
{
ParamBlock theBlock;
Rect sectRect;
unsigned long testMap;
short theRow,
theCount;
// Set up for call to SetupParamBlock (yeah yeah, I know)
theBlock.theRect = *theRect;
theBlock.userData = refCon;
theBlock.userProc = iconProc;
// If there is no one-bit data or the mask contains nothing but zeros, we return false
if (!SetupParamBlock(&theBlock) || !MakeBoundary(&theBlock))
return false;
// If there is alignment, we do it.
if (align)
PerformAlignment(&theBlock, align);
theRect = &(theBlock.theRect); // we hijak the parameter theRect to point to the possibly shifted destination rectangle
// (NOTE: This is very silly. The idea was to make references to theRect smaller and faster.
// Why not make a new variable, or not do this at all and refer to the rect as theBlock.theRect??
// We luck out under the 3.2 compiler, which sees that we refer to theRect a lot and allocates A3
// for this. If we were out of address registers, this would make larger and slower code)
PrescaleMask(&theBlock); // if the icon is to be shrunken, this routine will do the shrinking offscreen and place the
// result in maskMap
// we set up theRow, theCount and testMap to test against the pre-scaled mask data. theRow is equal to the first row of the maskMap
// that we will test. theCount is equal to the number of rows we will test. testMap is a long-word bitMap which we will intersect
// with each row that we test. If any row has intersection with testMap, then we have a hit.
if (IsRect)
{
// we do the intersection with the possibly shifted rectangle to see if alignment has moved us
// out of the test rect. (if there is no alignment, this is redundant with the SectRect we did
// in HitTestIconGeneric below. This probably needs restructuring in a new version.)
if (!SectRect((Rect *)pointOrRect, theRect, &sectRect))
return false;
// we set test map equal to one scan-line of the rectangle intersection of the test rect and the destination
testMap = 0xffffffff;
testMap <<= (32 - (sectRect.right - sectRect.left));
testMap >>= (sectRect.left - theRect->left);
// theRow is the top of the intersection rectangle in destination co-ordinates.
theRow = (sectRect.top - theRect->top);
// theCount is equal to the height of the intersection
theCount = (sectRect.bottom - sectRect.top);
}
else
{
// testMap is set to a one bit at the horizontal location of the test point in destination co-ordinates
testMap = 1 << (31 - ((short)(pointOrRect & 0xffff) - theRect->left));
// theRow is set equal to the vertical location of the test point in destination co-ordinates
theRow = ((short)(pointOrRect >> 16)) - theRect->top;
// there is only one row to test
theCount = 1;
}
// Here we take theRow, theCount and testMap and compare it to the mask data.
{
short rowBytes = theBlock.maskMap.rowBytes;
// since we treat rows that are sometimes only a word in width as long words, we need
// to mask off the right most data that is significant.
unsigned long significantMask = -1L << (32 - theBlock.maskMap.bounds.right);
unsigned char *maskData = theBlock.maskMap.baseAddr + ShortMul(rowBytes, theRow),
*endData = maskData + ShortMul(rowBytes, theCount);
// start at theRow and loop through theCount rows, testing for itersection with testMap
do {
if (*((unsigned long *)maskData) & significantMask & testMap)
return true;
maskData += rowBytes;
} while (maskData < endData);
}
return false;
}
// HitTestIconGeneric is called by assembly language wrappers in IconUtils.a to implement PtInIconID,
// PtInIconSuite, PtInIconMethod, RectInIconID, RectInIconSuite and RectInIconMethod. It takes a value
// which is either a point or pointer to a rect along with a flag indicating which one it is. It also
// takes a rect which is where the icon being hit-test was drawn and the alignment that was used. Finally,
// the generic call-back and refcon used in all the generic routines (PlotIconGeneric, IconToRgnGeneric, etc.)
// It returns true if the mask data as aligned and stretched intersects either the rect or the pixel to
// the lower right of the point. It returns false if there is no intersection or if any kind of error was
// encountered.
pascal Boolean HitTestIconGeneric(unsigned long pointOrRect,
Rect *theRect,
IconAlignmentType align,
long refCon,
IconRetriever iconProc,
short IsRect)
{
Boolean rval = false;
RgnHandle testRgn = NULL;
Rect sectRect;
if (IsRect)
{
// if the test rectangle does not even touch the destination rect, clearly there is no hit
if (!SectRect((Rect *)pointOrRect, theRect, &sectRect))
goto out;
// if the test rectangle completely engulfs the destination rectangle, clearly there is a
// hit and no further testing is required.
if (EqualRect(theRect, &sectRect))
goto trueOut;
}
else
if (!PtInRect(*((Point *)(&pointOrRect)), theRect)) // If our point is not in the ball-bark, we bail
goto out;
// The only time we would use IconToRgnGeneric to test pointOrRect would be if our maskMap needed scaling
// and we could not do the scaling off-screen with PrescaleMask. This is only true if the height or
// width of the destination rect is greater than 32 (the size of our off-screen buffer). Otherwise, we
// use HitTestIconmaskGeneric which is faster and dosen't move memory as much.
if (((theRect->right - theRect->left) > 32) || ((theRect->bottom - theRect->top) > 32))
{
testRgn = NewRgn();
if (IconToRgnGeneric(testRgn, theRect, align, refCon, iconProc))
goto out;
if (IsRect)
{
if (!RectInRgn((Rect *)pointOrRect, testRgn))
goto out;
}
else
if (!PtInRgn(*((Point *)(&pointOrRect)), testRgn))
goto out;
}
else if (!HitTestIconMaskGeneric(pointOrRect, IsRect, theRect, align, refCon, iconProc))
goto out;
trueOut:
rval = true;
out:
if (testRgn)
DisposeRgn(testRgn);
return rval;
}
// MakeIconData is called after DeviceList is initialized to set up the ExpandMem global structure used by the icon utilities.
// The structure is returned in the variable parameter iconCluts. (Named for historical reasons and not changed lately)
//
// This is the layout of the structure:
//
// #define Labels 8
// #define Sets 8
//
// typedef struct
// {
// Strings labelStrings; // array of eight string handles to label strings. On a black and
// // white Mac, the rest of the structure after this point is not allocated.
// long seeds[Labels * Sets]; // array of seeds for all the cluts (transformed and labeled)
// CTabHandle colorTables[Labels * Sets], // array of CTabHandles for all cluts
// originals[2]; // handles to the two original rom cluts (clut 4 and clut 8)
// RGBColor labelColors[Labels]; // array of 8 label colors
// unsigned char **indexLists[2]; // two handles to arrays of indices used in checking colors
// GDHandle virtualScreen; // a GDHandle used by SetIconDevice and GetIconDevice
// long deviceListSize; // number of GDevices at boot time
// DeviceCheck deviceCheckList[0]; // a variable number of DeviceCheck structures used to cache
// // the results of CheckColors for all GDevices
// } GlobalIconData;
//
// typedef Handle Strings[Labels];
//
// typedef struct
// {
// GDHandle theDevice; // Handle to the GDevice that we're caching the information about
// long theSeed; // the seed of its GDPmap's pmTable at last caching
// unsigned long checkMask, // a bit-map specifying which cluts we actually tried and cached results for
// checkResults, // results of caching as a bit-map
// disabledCheckMask, // the other long word of the CheckMask bit-map needed when we added disabled cluts
// disabledCheckResults; // the other long word of the CheckResults bit-map needed when we added disabled cluts
// } DeviceCheck;
pascal void MakeIconData(GlobalIconDataHandle *iconCluts)
{
long size,
devices;
GlobalIconDataHandle temp;
short curResFile, i;
Handle aHandle,
*strPtr;
RGBColor *colorPtr;
GDHandle aGDHandle;
DeviceCheck *checkPtr;
// If we're running on a color machine, allocate the whole structure. Otherwise, just allocate the
// label string list.
if (HaveCQD())
{
aGDHandle = DeviceList;
size = sizeof(GlobalIconData);
devices = 0;
do {
++devices;
size += sizeof(DeviceCheck);
aGDHandle = (GDHandle)((*aGDHandle)->gdNextGD);
} while (aGDHandle);
}
else
size = sizeof(Strings);
temp = (GlobalIconDataHandle)NewHandleSysClear(size);
HLock((Handle)temp); // temporarily lock the structure
*iconCluts = temp; // return the structure in the variable parameter
strPtr = &((*temp)->labelStrings); // point to the beginning of the label string handles
// Get the label strings (Should they be unloaded?)
for (i = labelColorBase; i < (labelColorBase + Labels); ++i)
*strPtr++ = GetResource('lstr', i);
// If we're not on a color machine, we're done.
if (!HaveCQD())
goto out;
// virtualScreen is initially NULL, indicating that we will traverse the list of devices normally.
(*temp)->virtualScreen = NULL;
// set deviceListSize with the count we generated for allocation
(*temp)->deviceListSize = devices;
// Run through the list of devices, initializing our cache of device information.
aGDHandle = DeviceList;
checkPtr = &((*temp)->deviceCheckList[0]);
do {
checkPtr->theDevice = aGDHandle;
checkPtr->theSeed = (*((*((*aGDHandle)->gdPMap))->pmTable))->ctSeed;
checkPtr->checkMask = 0L;
checkPtr->disabledCheckMask = 0L;
++checkPtr;
aGDHandle = (GDHandle)((*aGDHandle)->gdNextGD);
} while (aGDHandle);
// Save the current resource file and set to the System file.
curResFile = CurResFile();
UseResFile(0);
// Get the two lists of color indices to check at CheckColors time
strPtr = (Handle *)(&((*temp)->indexLists));
*strPtr++ = GetResource('indl', labelColorBase);
*strPtr = GetResource('indl', labelColorBase + 1);
// Get the two standard system clut's for 8 and 4 bits from ROM
strPtr = (Handle *)(&((*temp)->originals));
*((short *)RomMapInsert) = mapTrue;
*strPtr++ = GetResource('clut',8);
*((short *)RomMapInsert) = mapTrue;
*strPtr++ = GetResource('clut',4);
// Set the first label color to black.
colorPtr = &((*temp)->labelColors);
*((long *)(colorPtr)) = 0L;
colorPtr->blue = 0;
++colorPtr;
// Get the other seven label colors from resources
for (i = (labelColorBase + 1); i < (labelColorBase + Labels); ++i)
*colorPtr++ = **((RGBColor **)GetResource('rgb ', i));
// Restore the resource file
UseResFile(curResFile);
// Get the seed for the 8-bit system clut
strPtr = (Handle *)(&((*temp)->seeds));
*strPtr++ = (Handle)((*((*temp)->originals[0]))->ctSeed);
// Generate seeds for the other 7 labeled 8-bit cluts and the
// selected clut and the 7 labeled selected 8-bit cluts
for (i = 1; i < (Labels * 2); ++i)
*strPtr++ = (Handle)GetCTSeed();
// Get the see for the 4-bit system clut
*strPtr++ = (Handle)((*((*temp)->originals[1]))->ctSeed);
// Generate seeds for the other 7 labeled 4-bit cluts and the
// selected 4-bit clut and the 7 labeled selected 4-bit cluts and
// all the other combinations of labeled, selected and disabled cluts
for (i = 1; i < (Labels * 6); ++i)
*strPtr++ = (Handle)GetCTSeed();
// Allocate empty handles for all the cluts
strPtr = (Handle *)(&((*temp)->colorTables));
for (i = 0; i < (Labels * Sets); ++i)
{
aHandle = NewEmptyHandleSys();
*strPtr++ = aHandle;
}
out:
HUnlock((Handle)temp);
}
#define acceptableDelta (2 * 256)
// CompareComponent takes two color components and returns 0 if the two are equal within
// the tolerance specified by acceptableDelta, a negative number if the first is less than
// the second by acceptableDelta and a positive number if the first is greater than the
// second by acceptableDelta.
long CompareComponent(unsigned short comp1, unsigned short comp2)
{
long absoluteDifference = (unsigned long)comp1 - (unsigned long)comp2;
if ((absoluteDifference < acceptableDelta) && (absoluteDifference > -acceptableDelta))
return 0;
else
return absoluteDifference;
}
// Darken is used to reduce the brightness of an RGBColor to create selected clut's. The intention
// is to take a color, halve all the components and then increase the saturation of the result by
// halving again all but the strongest component. It is necessary to increase the saturation of
// a color when you halve its components because dividing all the components by two causes
// the difference in component values to become much smaller, decreasing the saturation. The alogrithm
// implemented below causes the HSV value of the color to be halved, while approximately maintaining
// the color's original saturation.
void Darken(RGBColor *aColor)
{
unsigned short *smallest = &(aColor->red),
*notSmallest1 = &(aColor->green),
*notSmallest2 = &(aColor->blue),
*temp = smallest;
long compareValue;
// halve all values
aColor->red >>= 1;
aColor->green >>= 1;
aColor->blue >>= 1;
// Gray has zero saturation, so it can't lose anything in
// the darkening process. Thus we are done in the grayish case.
if (!CompareComponent(*smallest, *notSmallest1) && !CompareComponent(*smallest, *notSmallest2))
return;
// Find the smallest color component.
if (CompareComponent(*smallest, *notSmallest1) > 0)
{
temp = smallest;
smallest = notSmallest1;
notSmallest1 = temp;
}
if (CompareComponent(*smallest, *notSmallest2) > 0)
{
temp = smallest;
smallest = notSmallest2;
notSmallest2 = temp;
}
// Divide its value by two again, so that the larger components are emphasized.
(*smallest) >>= 1;
// take the runner-up in the race for first place and divide it by two again so that
// the largest component is emphasized. If its a tie, we're done.
compareValue = CompareComponent(*notSmallest1, *notSmallest2);
if (compareValue < 0)
(*notSmallest1) >>= 1;
else if (compareValue > 0)
(*notSmallest2) >>= 1;
}
// Luminance takes an RGBColor and generates a value from 0 to 0xffff equal to the percieved
// gray-scale brightness of the color. The algorithm (which is the same one used by the QuickDraw
// to make such conversions) is to take a weighted average of all the components, where the weights
// are 1/8 of the blue component, 5/16 of the red component and 9/16 of the green component. The routine
// is used by Brighten to decide how much to brighten a color.
unsigned long Luminance(RGBColor *aColor)
{
unsigned long temp;
temp = ((unsigned long)aColor->red) << 2; // five times the red component
temp += aColor->red;
temp += ((unsigned long)aColor->green) << 3; // plus nine times the green component
temp += aColor->green;
temp += ((unsigned long)aColor->blue) << 1; // plus two times the blue component
return (temp >> 4); // divide the sum by 16 to get the weighted average
}
// Brighten takes an RGBColor, reduces its saturation and increases its brightness (virtually the inverse
// of Darken). It is used by MakeClut to take a raw label color and convert it into a color which can be
// applied to a clut and used to label 8-bit icons. Brightened colors will not seriously affect the
// brightness of the icon's unselected appearance, but will retain much of the character of the original color.
//
// A little background: A label color is applied to a clut color by multiplying each component by the
// percentage strength of the corresponding label color component. For example, a label color of
// {0x7fff, 0x3fff, 0xffff} would be interpreted as 50% red, 25% green, 100% blue and each clut color
// would have its components multiplied by the corresponding fraction. Since the standard label colors
// vary greatly in their effect on a clut color and since a label color can be customized by the user to
// any value, a label color must be processed so that it does not reduce the brightness of an affected
// icon to the point where it looks selected or unreadably dark. An ideal label color has low HSV saturation
// so that no color component is so attenuated that it becomes very dark. A pure blue for example is
// 100% saturated and would reduce all components except blue to zero, making for a very dark icon. A low
// saturation color has a lot of gray in it, thus allowing some representation from all colors in the icon.
// A good label color should also have a value (in the HSV sense of the word value) of 100%; that is, it should
// have at least one component that is 100%, so that the overall value of the resultant color is not greatly reduced.
// Finally, whatever process is used to turn a bad label color into a good label color should preserve the HSV hue
// of the color, since that is what gives the color its "character". A further constraint on the algorithm to make
// any label color usable is that it be sensitive to the hue. For example, an unprocessed blue label color of a
// certain saturation and value will make an icon look darker than an unprocessed green label color of the same
// saturation and value, because the human eye is many times more sensitive to green than to blue. Thus a blue
// label color needs to have its saturation reduced more than a green label color.
//
// The algorithm: The processing of any arbitrary color into a good label color takes place conceptually in three
// steps (although it it implemented in one pass through the components). The first step is to increase the HSV
// value of the color to 100%, while nearly maintaining the hue. This is done by multiplying each component by a
// factor which takes the maximum component to 0xffff, thus "stretching" the color to its maximum value.
// The second step multiplies the color by a fraction related to the original luminance of the label color. This
// makes the color closer to a gray (i.e. lowers its saturation) and incidentally lowers its over all value (brightness).
// The third step compensates for the incidental loss of brightness by adding in a constant value, which is the complement
// of the compression factor. The maximum component in the resulting color is 100%, the saturation has been lowered to
// an extent related to the luminance and the hue is maintained.
void Brighten(RGBColor *labelColor)
{
unsigned short *component = (unsigned short *)labelColor,
*end = component + 3;
unsigned long temp,
luminance = Luminance(labelColor);
unsigned short basis,
max = labelColor->red;
// Find the maximum component
if (labelColor->green > max)
max = labelColor->green;
if (labelColor->blue > max)
max = labelColor->blue;
// This is the ultimate brightening. Black gets mapped to white. This implements the "feature"
// that specifying black as a label color means "label this with a text only. Don't apply a color
// to this icon." White will in no way affect the appearance of the icon. This was the lowest impact
// change to implement this feature. A more optimal solution in terms of performance would be to
// bail in PlotIconGeneric when we see a black label color. This implies that we should store the
// label color in the parameter block (since we're going to all the trouble of getting it so early on
// in the plot process). This involves some broad changes in labeling code which did not seem warranted
// at B4 time.
if (!max)
{
*((long *)labelColor) = -1;
labelColor->blue = 0xffff;
return;
}
// here we calculate the parameters basis and luminance. luminance is related to the gray-scale brightness
// of the color being brightened, but is compressed to range from 0x6000 to 0xffff. Basis is the complement
// of luminance and ranges from 0x9fff to 0. Calculating the values in this way makes the process of
// brightening sensitive to the color being brightened. If the luminance of the color is low, the basis
// will be large and their will be a greater reduction in saturation.
//
// NOTE: the extent to which luminance is compressed into a smaller range ensures that there is a minimum
// saturation decrease. In the code below, luminance is multiplied by 5/8 and 3/8 of 100% (0x6000) is added
// on to it to achieve the compression. If a fraction smaller the 5/8 is used, the overall saturation of all
// labeled colors will increase. Multiplying luminance by 1/2 and adding 0x8000 produces labeled icons which
// are slightly to dark. Some observers have noted that the current settings produce icons whose labeling
// does not sufficiently suggest the color chosen from the Finder labels menu. This is an area that bears
// some further investigation.
luminance >>= 1; // halve luminance
luminance += (luminance >> 2); // add a quarter of the halved luminance to the halved luminance to get 5/8 luminance
luminance += 0x6000; // add about 3/8 of the whole range to the number
basis = 0xffff - luminance;
// This loop uses the calculated parameters luminance and basic to perform the brightening.
do {
temp = (unsigned long)(*component);
// stretch the color. If the maximum component is already 100%, skip this step for all components
if (max < 0xffff)
{
temp *= 0xffff;
temp /= max; // max will only be zero if the label color is black in which case we will not reach this point
}
// compress the color
temp *= luminance;
temp /= 0xffff;
// add in a gray component
*component++ = basis + (unsigned short)temp;
} while (component < end);
// NOTE: for a future version, the following loop body would have been much better. On the other hand, the current
// version makes the underlying model more clear and performance is not particularly critical in this routine.
//
// temp = (unsigned long)(*component);
//
// if (temp < 0xffff)
// {
// temp *= luminance;
// temp /= max;
// *component = basis + (unsigned short)temp;
// }
//
// ++component;
}
// FindBlack is called by MakeClut to find the lowest luminance color in a clut. Why? It's for the 4-bit labeled true CIcon
// (don't laugh). When such an icon is labeled, the rule is that I turn the black color to the label color. In an Icon
// suite, the black color we want is well-defined as the 15 entry. In a CIcon, there may be no real black, so I'll have to
// choose something to label. Might as well be the closest thing to black. NOTE: a next version of this code should check
// the last entry in the clut to see if it is black, and if so it should return clutSize. If the last entry is black, that's
// the one we want. A good way to accomplish this would be to do the search from the last entry backward, bailing if any
// color is black (something this code should do anyway), taking the minimum luminance value otherwise.
long FindBlack(CTabHandle theClut, long clutSize)
{
ColorSpec *specList = (*theClut)->ctTable;
unsigned long luminance,
minLuminance = 0xffff;
long rval = 0,
index = 0;
do {
luminance = Luminance(&(specList->rgb));
if (luminance < minLuminance)
{
minLuminance = luminance;
rval = clutSize;
}
++index;
++specList;
} while (index <= clutSize);
return rval;
}
// MakeClut is called by CheckClut if IconGetClut passes it a purged clut or by GetBestData if IconGetClut returns a purged clut.
// The clut is reallocated, a copy of the ROM clut of the appropriate bit-depth (4 or 8) is copied into it, it is given
// its old seed back and is then processed according to the transform and label. MakeClut is also called by
// SetupParamBlockForCIcon to process a copy of the CIcon's clut to do transforms.
void MakeClut(ParamBlock *theBlock)
{
short labelNumber = theBlock->clutNumber; // 0-7, which label, if any, we are using
CTabHandle theClut = theBlock->theClut; // the purged clut handle we are rebuilding
short sourceNumber; // 0 or 1, specifies which of the ROM clut's we will use for a source
unsigned char *listPtr; // pointer to the list of clut indices to be protected with CheckColors
long blackEntry, // index of the color we will consider black for this clut (see FindBlack above)
entryNo,
clutSize; // number of entries in the processed clut
ColorSpec *specList;
RGBColor aColor,
labelColor;
// If we're recreating a purged IconSuite clut, we will use the clut database in our ExpandMem
// globals for the source. If we are processing a copy of a CIcon clut, we get the clut from
// our parameter block.
if (theBlock->theGroup != aCIcon)
{
GlobalIconDataPtr theGlobals = GetGlobalPtr();
long theSeed = theGlobals->seeds[labelNumber];
CTabHandle source;
unsigned char **theList;
unsigned long size;
// this convoluted arithmetic of getting a labelNumber and sourceNumber from the
// clutNumber (as opposed to just looking at the theLabel field) comes from the fact
// that theLabel specifies what label the user asked for, not necessarily the one
// we're going to give him. For example, if the plotting takes place on a gray screen,
// the label is ignored. The clutNumber reflects this fact, and theLabel does not.
// I could have had more fields in the ParamBlock to figure all this out, but they would
// have to be set up by IconGetClut. In general, it is more important for IconGetClut to be fast,
// since in most cases, the clut will be already created and cached. We now return you
// to your regularly scheduled programming.
// If the transform is ttDisabled, labelNumber will be in the range of disabled clut numbers.
// This line brings it back into the lower range.
if (theBlock->theTransform == ttDisabled)
labelNumber -= ((Labels * Sets) >> 1);
// If the clut is in the 4-bit range, bring it into the lower range and record the fact that
// we will be building a 4-bit clut.
if (labelNumber >= (Labels << 1))
{
labelNumber -= (Labels << 1);
sourceNumber = 1;
blackEntry = 15; // Only used if we are processing 4-bit cluts. For non-CIcons blackEntry is always 15
}
else
sourceNumber = 0;
labelNumber >>= 1; // There is a selected and non-selected version of every clut. Dividing by two puts
// labelNumber into the range 0-7.
source = theGlobals->originals[sourceNumber]; // get a handle to the source clut
theList = theGlobals->indexLists[sourceNumber]; // get a handle to the list of indices to protect
size = myGetHandleSize((Handle)source); // get the size to reallocate the purged clut
clutSize = (*source)->ctSize; // get the number of entries in the clut
ReallocHandle((Handle)theClut, size); // reallocate the clut
HPurge((Handle)theClut); // make clut purgable
BlockMove((Ptr)(*source), (Ptr)(*theClut), size); // copy in the source clut
(*theClut)->ctSeed = theSeed; // restore its unique seed
listPtr = *theList; // dereference the index list handle
}
else
{
// a CIcon's clut is copied and processed each time it is plotted (to each screen if on multiple monitors) Thus,
// the values used to determine the parameters used in processing are determined differently.
listPtr = NULL; // CIcon cluts are never checked with CheckColors so listPtr is irrelevant
clutSize = (*theClut)->ctSize; // theClut is already a copy so whatever we take whatever size it is
// For a CIcon, sourceNumber is used only to mean "Shall we process this clut in the 4-bit style (1) or the
// 8-bit style (0)"
if (theBlock->screenDepth < 8)
{
sourceNumber = 1;
blackEntry = FindBlack(theClut, clutSize);
}
else
sourceNumber = 0;
}
// If there's a label get the RGBColor
if (labelNumber)
{
GetLabel(labelNumber, &labelColor, NULL);
// If we're processing an 8-bit clut, we Brighten the color. We don't do it in four bits because there's very little
// hope of achieving the subtle color effects that we can in 8-bits. The labeling algorithm in 4-bits is different
// anyway and does not require the brightening process. (See Brighten above)
if (!sourceNumber)
Brighten(&labelColor);
}
specList = (*theClut)->ctTable; // point to the beginning of the list of ColorSpecs in theClut
// Loop through all the clut entries, doing various kinds of color arithmetic to achieve the
// transformed clut effects.
for (entryNo = 0; entryNo <= clutSize; ++entryNo)
{
// we only do labeling and selection processing on clut entries in the index list. That way,
// an icon designer could decide to use some colors for detail outside this list and not have
// them labeled or selected. Thus, if we have a listPtr, we check if this entry is in the list
// before processing. We process all colors otherwise (for CIcons).
if ((!listPtr) || (entryNo == *listPtr))
{
// If we have a listPtr, increment it.
if (listPtr)
++listPtr;
aColor = specList->rgb; // make a copy of the color to process
// if there is a label color, apply it to this clut entry.
if (labelNumber)
{
// if the clut is 4-bit, set the black entry of the clut to the label color.
// Otherwise, tint the entire clut to the brightened label color by multiplying
// each component of each color by the fractional strength of the corresponding
// component of the label color. The net effect of this process is to "filter"
// the clut entry through the label color.
if (sourceNumber)
{
if (entryNo == blackEntry)
aColor = labelColor;
}
else
{
if (entryNo)
{
// multiplying the components together in this fashion has the effect of "filtering"
// the entry color through the label color.
aColor.red = (aColor.red * (unsigned long)(labelColor.red)) >> 16;
aColor.green = (aColor.green * (unsigned long)(labelColor.green)) >> 16;
aColor.blue = (aColor.blue * (unsigned long)(labelColor.blue)) >> 16;
}
else
{
// if possible, the white entry has to be "brighter" than any other color, so instead of
// multiplying white by the label color (which would simply produce the label color),
// we bias the label color toward white and use that.
aColor.red = ((unsigned long)(aColor.red) + labelColor.red) >> 1;
aColor.green = ((unsigned long)(aColor.green) + labelColor.green) >> 1;
aColor.blue = ((unsigned long)(aColor.blue) + labelColor.blue) >> 1;
}
}
}
// If this clut is for a selected icon, we apply the darkening algorithm to every affected entry
if (theBlock->selected)
Darken(&aColor);
specList->rgb = aColor; // record the processed color
}
else
specList->value |= 4; // If this color is not in the protected list, we set the inhibit bit which causes
// CheckColors to ignore this entry.
// If the transform is ttDisabled, we average the color with white. This has the affect of "bleaching" the color,
// making it seem "grayed" in the same sense that disabled text is shown.
if (theBlock->theTransform == ttDisabled)
{
aColor = specList->rgb;
aColor.red = ((unsigned long)(aColor.red) + 0xffff) >> 1;
aColor.green = ((unsigned long)(aColor.green) + 0xffff) >> 1;
aColor.blue = ((unsigned long)(aColor.blue) + 0xffff) >> 1;
specList->rgb = aColor;
}
++specList; // Get the next entry in the clut
}
}
// GetDeviceCheck is called by IconGetClut to get the DeviceCheck structure corresponding to theDevice.
// The DeviceCheck structure is used to cache information about which cluts have been checked against
// a certain GDevice's available colors.
//
// A little background: CheckColors is a palette manager routine that takes a CTabHandle, a GDevice and some
// tolerance values and returns a boolean, indicating whether the clut's colors can be represented on that device
// to the specified tolerance. To avoid calling CheckColors every time we plot, we cache the results of the
// check in a list of structures stored at the end of our ExpandMem globals. There is one structure for every
// GDevice in the DeviceList. Each structure contains a GDHandle to the corresponding device, the seed of the
// device's color table at the time of the last check and two 64-bit bit-maps, each corresponding to the 64
// different cluts used in plotting color icons. One of the bit-maps indicates whether the clut has been
// checked. The other bit-map indicates the result (good or bad) of the check. Before a clut is returned by
// IconGetClut, the DeviceCheck for the given GDevice is consulted to see if the clut has already been checked
// for that device, and if so, what the result was. If the clut has never before been checked, CheckColors
// is called, the DeviceCheck structure is updated to indicate that the clut has been checked and the
// result is cached.
DeviceCheck *GetDeviceCheck(GDHandle theDevice, GlobalIconDataPtr theGlobals)
{
DeviceCheck *theCheck = &(theGlobals->deviceCheckList[0]); // prime the pointer to the beginning of deviceCheckList
long listSize = theGlobals->deviceListSize,
theSeed = (*((*((*theDevice)->gdPMap))->pmTable))->ctSeed; // Get the device's ColorTable's seed
// Loop through all the DeviceCheck structures in the list, looking for the one corresponding to theDevice
do {
if (theCheck->theDevice == theDevice)
{
// if we find a device, check if its seed has changed since the last check we did. If it has, none of
// the cached check values are valid, so we invalidate the cache and update theSeed.
if (theCheck->theSeed != theSeed)
{
theCheck->theSeed = theSeed;
theCheck->checkMask = 0L;
theCheck->disabledCheckMask = 0L;
}
return theCheck;
}
--listSize;
++theCheck;
} while (listSize);
return NULL;
}
#define iconMaxTol 0x4000
#define iconMaxAveTol 0x2000
// CheckClut takes a parameter block and calls CheckColors on theClut. If theClut is purged,
// we rebuild it first. (NOTE: I don't know why this routine returns a long. Should be a Boolean)
long CheckClut(ParamBlock *theBlock)
{
if (!(*(theBlock->theClut)))
{
// IconGetClut assumes that our ExpandMem block won't move. Since MakeClut can move memory, we lock
// down our global block before calling and unlock afterward.
GlobalIconDataHandle GlobalDataHandle = GetGlobalHandle();
HLock((Handle)GlobalDataHandle);
MakeClut(theBlock);
HUnlock((Handle)GlobalDataHandle);
}
// the tolerances used here mean that any given color can be represented if the device has a
// color within 25% of it. The average tolerance of all colors in the clut must be within
// 12.5%. These values are empirically derived, but otherwise arbitrary.
return CheckColors(theBlock->theDevice, theBlock->theClut, iconMaxTol, iconMaxAveTol);
}
// IconGetClut gets the clut that should be used to plot an icon described by theBlock using data
// of the theDepth depth. On entry, theBlock has theDevice, theTransform, selected and theLabel
// fields set up. On exit, theBlock has theClut and clutNumber set up. IconGetClut returns false if
// the clut for the icon plotted to theDepth does not pass the CheckColors test for the given
// device. (See GetDeviceCheck above). The basic procedure for finding the clut is to generate
// an index into the array of cluts in the ExpandMem globals from the information in theBlock and
// theDepth. The clut is then checked, using the cached information in the deviceCheckList (See
// GetDeviceCheck above) or CheckClut (see above) if necessary. If the clut passes the test we
// return true, otherwise we return false. (NOTE: again this function should return a boolean
// in stead of a long)
long IconGetClut(ParamBlock *theBlock, short theDepth)
{
GlobalIconDataPtr theGlobals = GetGlobalPtr();
short clutNumber; // index into clut array
unsigned long clutMask,
*theCheckMap,
*theResult;
DeviceCheck *theCheck;
GDHandle theDevice = theBlock->theDevice;
// if we are not on a gray-scale monitor, clutNumber is set to twice the label number (because
// there are two cluts for each label, selected and not selected). No labeling is attempted
// on a gray-scale monitor.
if ((theDevice && (!((*theDevice)->gdFlags & 0x1))))
clutNumber = 0;
else
clutNumber = theBlock->theLabel << 1;
// Even numbered cluts are unselected, odd numbered cluts are selected.
if (theBlock->selected)
++clutNumber;
// The second half of the array contains transformed cluts
if (theBlock->theTransform == ttDisabled)
clutNumber += ((Labels * Sets) >> 1);
// the 4-bit cluts follow the 8-bit cluts in the transformed and untransformed groups
if (theDepth < 8)
clutNumber += (Labels << 1);
// Get the clut and record it and its index in theBlock.
theBlock->theClut = theGlobals->colorTables[clutNumber];
theBlock->clutNumber = clutNumber;
// theDevice will be NULL if we are plotting to a printer or to pict. If so,
// we don't do any checks.
if (!theDevice)
return true;
// We get the DeviceCheck structure corresponding to the device we're checking. If GetDeviceCheck
// returns NULL, it means that the caller is plotting to an off-screen GDevice (which is OK and will
// work). We won't have any information cached for such a device so we call CheckClut immediately.
if (theCheck = GetDeviceCheck(theDevice, theGlobals))
{
// get clutNumber into the 0-31 range and set theCheckMap and theResult to either the disabled
// or non-disabled check bit-maps (See GetDeviceCheck above for a general description of
// of these bit-maps)
if (clutNumber >= ((Labels * Sets) >> 1))
{
clutNumber -= ((Labels * Sets) >> 1);
theCheckMap = &(theCheck->disabledCheckMask);
theResult = &(theCheck->disabledCheckResults);
}
else
{
theCheckMap = &(theCheck->checkMask);
theResult = &(theCheck->checkResults);
}
// create a one-bit mask to check against the bit-maps
clutMask = 1 << clutNumber;
// if the clut has never been checked against this device, update theCheckMap, call CheckClut
// and record the result.
if (!((*theCheckMap) & clutMask))
{
(*theCheckMap) |= clutMask;
if (CheckClut(theBlock))
(*theResult) |= clutMask;
else
(*theResult) &= ~clutMask;
}
// if the result of the check was that this clut cannot be well-represented on this device,
// return false
if (!((*theResult) & clutMask))
return false;
}
else
return CheckClut(theBlock);
return true;
}
// __GetLabel is used to get information about the system labels. It is called by the
// trap dispatcher to implement the public GetLabel call. labelNumber specifies which
// label we are querying. labelColor and labelString are buffers into which store the
// label's RGB value and text, respectively. Either labelColor or labelString can be
// NULL to indicate that no information is required about the color or the text.
pascal OSErr __GetLabel(short labelNumber, RGBColor *labelColor,
Str255 labelString)
{
GlobalIconDataPtr theGlobals;
// if labelNumber is out of range we return an error
if ((labelNumber < 0) || (labelNumber > 7))
return paramErr;
// Get a pointer to the global parameter block hanging off of ExpandMem
theGlobals = GetGlobalPtr();
// If we don't have color QuickDraw, the global parameter block will not
// contain any label color information. (Should this return an error?)
if (labelColor && HaveCQD())
*labelColor = theGlobals->labelColors[labelNumber];
if (labelString)
{
Handle aHandle = theGlobals->labelStrings[labelNumber];
LoadResource(aHandle);
if (ResErr)
return ResErr;
BlockMove((Ptr)(*aHandle), (Ptr)labelString, (long)(**aHandle) + 1);
}
return noErr;
}
// InvalLabelCluts is called by SetLabel to purge all the icon-plotting cluts associated with a
// label whose color has been changed
#define EightBitNonSelected 0
#define EightBitSelected 1
#define FourBitNonSelected (Labels * 2)
#define FourBitSelected (FourBitNonSelected + 1)
#define DisabledEightBitNonSelected ((Labels * Sets) / 2)
#define DisabledEightBitSelected (DisabledEightBitNonSelected + 1)
#define DisabledFourBitNonSelected (DisabledEightBitNonSelected + (Labels * 2))
#define DisabledFourBitSelected (DisabledFourBitNonSelected + 1)
pascal void InvalLabelCluts(short labelNumber)
{
GlobalIconDataHandle GlobalDataHandle = GetGlobalHandle();
CTabHandle *clutPtr;
HLock((Handle)GlobalDataHandle);
// Get a pointer to the unselected, non-disabled, 8-bit CTabHandle for this label in the
// global array of icon-plotting cluts and purge it.
clutPtr = &((*GlobalDataHandle)->colorTables[labelNumber << 1]);
EmptyHandle((Handle)(*clutPtr + EightBitNonSelected));
// purge the 8-bit, selected clut
EmptyHandle((Handle)(*(clutPtr + EightBitSelected)));
// purge the 4-bit non-selected clut
EmptyHandle((Handle)(*(clutPtr + FourBitNonSelected)));
// purge the 4-bit selected clut
EmptyHandle((Handle)(*(clutPtr + FourBitSelected)));
// purge the 8-bit, non-selected, disabled clut
EmptyHandle((Handle)(*(clutPtr + DisabledEightBitNonSelected)));
// purge the 8-bit, selected, disabled clut
EmptyHandle((Handle)(*(clutPtr + DisabledEightBitSelected)));
// purge the 4-bit, non-selected, disabled clut
EmptyHandle((Handle)(*(clutPtr + DisabledFourBitNonSelected)));
// purge the 4-bit, selected, disabled clut
EmptyHandle((Handle)(*(clutPtr + DisabledFourBitSelected)));
HUnlock((Handle)GlobalDataHandle);
}
// __SetLabel is used to get information about the system labels. It is called through the
// trap dispatcher to implement the GetLabel call, which is exported but private. labelNumber
// specifies which label we are modifying. labelColor and labelString are buffers containing the
// label's RGB value and text, respectively. Either labelColor or labelString can be
// NULL to indicate that the color or text is not to be changed.
pascal OSErr __SetLabel(short labelNumber, RGBColor *labelColor,
Str255 labelString)
{
GlobalIconDataPtr theGlobals;
Handle aHandle;
// If the labelNumber is out of range, return a paramErr. Note that the range of valid
// label numbers is different for __SetLabel than for __GetLabel. Label 0 cannot be
// changed and is always black with text "None".
if ((labelNumber < 1) || (labelNumber > 7))
return paramErr;
// If labelColor is specified and the global data structure contains color information,
// set the label value.
if (labelColor && HaveCQD())
{
// Update the list of label colors in memory
theGlobals = GetGlobalPtr();
theGlobals->labelColors[labelNumber] = *labelColor;
// Update the label color in the system file
aHandle = GetResource('rgb ', labelColorBase + labelNumber);
*((RGBColor *)(*aHandle)) = *labelColor;
HNoPurge(aHandle);
ChangedResource(aHandle);
WriteResource(aHandle);
HPurge(aHandle);
// Invalidate cluts associated with labelNumber
InvalLabelCluts(labelNumber);
}
// if there is a label string to set…
if (labelString)
{
unsigned char size = *labelString + 1;
// if the string is larger than a Str32, truncate it
if (size > 32)
size = 32;
// update the string in memory.
theGlobals = GetGlobalPtr();
aHandle = theGlobals->labelStrings[labelNumber];
LoadResource(aHandle);
BlockMove((Ptr)labelString, *aHandle, (long)size);
**aHandle = size-1;
// update the system file
HNoPurge(aHandle);
ChangedResource(aHandle);
WriteResource(aHandle);
HPurge(aHandle);
}
UpdateResFile(0);
return noErr;
}
// These two routines are used as call-backs for PlotIconGeneric to implement
// PlotIconHandle, PlotSICNHandle
Handle FromICON(long data, IconType theIcon)
{
if (theIcon == largeIcon1)
return ((Handle)data);
else
return NULL;
}
Handle FromSICN(long data, IconType theIcon)
{
if ((theIcon == smallIcon1) || (theIcon == miniIcon1))
return ((Handle)data);
else
return NULL;
}
// These two routines are called through the trap dispatcher to implement the public
// calls PlotIconHandle and PlotSICNHandle. NOTE: It was my intention to move these
// and their respective call-backs to IconUtils.a for speed and size efficiency.
pascal OSErr __PlotIconHandle(const Rect *theRect,
short align,
short transform,
Handle theIcon)
{
return PlotIconGeneric(theRect, align, transform, (long)theIcon, FromICON, false);
}
pascal OSErr __PlotSICNHandle(const Rect *theRect,
short align,
short transform,
Handle theIcon)
{
return PlotIconGeneric(theRect, align, transform, (long)theIcon, FromSICN, false);
}
// The QuickDraw.h definition of a CIcon includes a one-byte array at the end as a place-holder
// for the variable-sized mask data. This is a CIcon definition that includes only the fixed-size
// data for a sizeof() operator.
typedef struct
{
PixMap iconPMap; /*the icon's pixMap*/
BitMap iconMask; /*the icon's mask*/
BitMap iconBMap; /*the icon's bitMap*/
Handle iconData; /*the icon's data*/
} myCIcon;
// SetupParamBlockForCIcon initializes a parameter block for a true CIcon. It is called by
// __PlotCIconHandle after the theBlock has its theRect, userProc, theTransform, theLabel
// selected and dontDraw fields initialized. On exit theBlock is setup to be called by
// PlotTheCIcon (See below).
void SetupParamBlockForCIcon(ParamBlock *theBlock)
{
// the userProc field is used to hold a pointer to the locked CIcon structure
CIconPtr iconPtr = (CIconPtr)(theBlock->userProc);
short height = iconPtr->iconMask.bounds.bottom - iconPtr->iconMask.bounds.top;
unsigned long maskDataSize = (iconPtr->iconMask.rowBytes * height);
// oneBitImageHandle is a Handle which we will set up to take the place of the one-bit
// member of an icon suite. We will be setting up maskMap to point to the mask data
// within the CIcon itself, so we only need to make oneBitImageHandle as big as the
// one-bit image data.
Handle oneBitImageHandle = NewHandle(maskDataSize);
// theOneBitData points to the one-bit data (one-bit image and mask) for this CIcon.
// oneBitPtr is the beginning of the one-bit image data. It is initialized to theOneBitData
// because it is legal to omit the one-bit image data from a CIcon, in which case we use
// the mask as the one-bit data.
unsigned char *theOneBitData = (unsigned char *)(iconPtr->iconMaskData),
*oneBitPtr = theOneBitData;
RgnHandle maskRgn = NewRgn();
GrafPtr curPort = myGetPort();
HLock(oneBitImageHandle);
// If there is one bit-data, we point oneBitPtr to it. Otherwise, we leave it pointing
// to the mask data.
if (theBlock->userData > (sizeof(myCIcon) + maskDataSize))
oneBitPtr += maskDataSize;
// copy the one-bit image data to the simulated one-bit suite entry and point the theMask
// field in the paramBlock to point at it.
BlockMove((Ptr)oneBitPtr, *oneBitImageHandle, maskDataSize);
theBlock->theMask = oneBitImageHandle;
// copy the bounds and rowBytes information from the CIcon's iconMask into theBlock->maskMap
// and point the baseAddr field to the mask data within the CIcon
BlockMove((Ptr)(&(iconPtr->iconMask)), (Ptr)(&(theBlock->maskMap)), sizeof(BitMap));
theBlock->maskMap.baseAddr = (Ptr)theOneBitData;
// copy all the PixMap information from the CIcon into the PixMap within theBlock and set
// the theData field to point to the color data from the CIcon.
BlockMove((Ptr)(&(iconPtr->iconPMap)), (Ptr)(&(theBlock->dataMap)), sizeof(PixMap));
theBlock->theData = iconPtr->iconData;
// We use BitMapToRegion to generate the mask region used in doing transforms, etc.
// From this region we also derive our boundary information. userData is overloaded for
// CIcons to contain the handle to the mask region, since unlike icon suites, this region
// is generated once as opposed to when needed.
BitMapToRegion(maskRgn, &(theBlock->maskMap));
theBlock->boundary = (*maskRgn)->rgnBBox;
theBlock->userData = (long)maskRgn;
// Initialize miscelaneous fields for plotting
theBlock->onScreen = ((PrintVars == -1) && (!(curPort->picSave)));
theBlock->dstMap = &(curPort->portBits);
theBlock->dataMap.pixelSize = 0;
theBlock->theClut = NULL;
theBlock->rowBytes = theBlock->maskMap.rowBytes;
theBlock->useRegions = true; // we don't have any choice with CIcon's
}
// myColorTable is a ColorTable with only the fixed-sized portion for a sizeof() operator
typedef struct
{
long ctSeed; /*unique identifier for table*/
short ctFlags; /*high bit: 0 = PixMap; 1 = device*/
short ctSize; /*number of entries in CTTable*/
} myColorTable;
// CopyClut is called by GetCIconClut to create a clut that is the same as input clut but
// padded to the depth of the pixMap data to which it belongs. Thus a 5-entry clut for a
// 4-bit data pixMap would be padded to 16 entries. If a clut needs padding, the entries
// not specified are set to a ramp between white and black. This is to simulate the fact
// that relative colors in a CIcon are assigned to a ramp between foreGround and backGround.
// Since we always set foreGround to black and backGround to white, this will have the desired effect.
CTabHandle CopyClut(CTabHandle theClut, short depth)
{
long specArraySize = sizeof(ColorSpec) << depth; // size of the CSpecArray in new clut
CTabHandle theNewClut = (CTabHandle)NewHandle(sizeof(myColorTable) + specArraySize);
CTabPtr oldClutPtr = *theClut,
newClutPtr = *theNewClut;
short newClutEntries = (1 << depth) - 1, // ctSize of new clut
oldClutEntries = oldClutPtr->ctSize; // ctSize of old clut
newClutPtr->ctSeed = GetCTSeed(); // get unique seed
newClutPtr->ctSize = newClutEntries; // initialize ctSize entry
newClutPtr->ctFlags = oldClutPtr->ctFlags; // copy flags from old clut to new
// copy common data between the two cluts (yeah, and some garbage as well)
BlockMove((Ptr)(oldClutPtr->ctTable), (Ptr)(newClutPtr->ctTable), specArraySize);
// if there is no difference in CSpecArray size, we are done now.
if (newClutEntries > oldClutEntries)
{
ColorSpec tempColor,
*theTable = newClutPtr->ctTable;
*((long *)(&(tempColor.rgb))) = 0; // set the last entry to black
tempColor.rgb.blue = 0;
tempColor.value = newClutEntries;
theTable[newClutEntries] = tempColor;
++oldClutEntries; // we are now looking one entry beyond the
// last entry in the old clut
if (newClutEntries > oldClutEntries) // Is there more than one extra spot to fill?
{
long index;
unsigned short increment = 0xffff / (newClutEntries - oldClutEntries),
value = 0;
*((long *)(&(tempColor.rgb))) = -1; // set entry after the last specified to white
tempColor.rgb.blue = 0xffff;
tempColor.value = oldClutEntries;
theTable[oldClutEntries] = tempColor;
// fill all intermediate entries with an evenly spaced ramp from white to black
for (index = newClutEntries - 1; index > oldClutEntries; --index)
{
value += increment;
tempColor.rgb.red = value;
tempColor.rgb.green = value;
tempColor.rgb.blue = value;
tempColor.value = index;
theTable[index] = tempColor;
}
}
}
return theNewClut;
}
// This is the CIcon couterpart to IconGetClut. It is called by PlotTheCIcon (Below) to get
// the appropriate clut to use for plotting to deep devices.
pascal void GetCIconClut(ParamBlock *theBlock, GDHandle theDevice, short flags)
{
CTabHandle theClut = ((CIconPtr)(theBlock->userProc))->iconPMap.pmTable;
short clutDepth = ((CIconPtr)(theBlock->userProc))->iconPMap.pixelSize,
clutNumber;
// copy the CIcon's clut and pad if necessary
theBlock->theClut = CopyClut(theClut, clutDepth);
// clutNumber is either set to the label number or zero if we're on a gray-scale device.
// MakeClut (See above) uses this value differently for CIcons than for icon suites
if (theDevice && (!(flags & 0x1)))
clutNumber = 0;
else
clutNumber = theBlock->theLabel;
theBlock->clutNumber = clutNumber;
// process the copied clut appropriately for our device, transform and label
MakeClut(theBlock);
}
// PlotTheCIcon is called directly by __PlotCIconHandle (Below) or indirectly via DeviceLoop.
// It is responsible for deciding whether the color icon data will be plotted or the one-bit data.
// When PlotTheCIcon is called from DeviceLoop, we make use of DeviceLoop's setting up of visRgn to
// clip the image appropriately. (In PlotTheIcon, we do our own clipping and only use DeviceLoop
// to iterate through the active devices.)
pascal void PlotTheCIcon(short screenDepth,
short flags,
GDHandle theDevice,
ParamBlock *theBlock)
{
theBlock->screenDepth = screenDepth;
theBlock->theDevice = theDevice;
// if our monitor is deeper than four bits, or if our monitor is four bits and our icon is
// not selected or disabled, then we can plot the deep data. Otherwise we plot the one-bit
// data.
if (((screenDepth > 4) && (theBlock->theTransform != ttOpen)) ||
((screenDepth == 4) && (!(theBlock->selected)) && (!(theBlock->theTransform & ttDisabled))))
{
theBlock->dataDepth = ((CIconPtr)(theBlock->userProc))->iconPMap.pixelSize; // set up dataDepth for PlotDeep
GetCIconClut(theBlock, theDevice, flags); // get a processed copy of the clut
PlotDeep(theBlock); // plot the icon
DisposHandle((Handle)(theBlock->theClut)); // dispose of our copy of the clut
theBlock->theClut = NULL;
}
else
{
theBlock->dataDepth = 1;
theBlock->theClut = NULL;
PlotShallow(theBlock);
}
}
// __PlotCIconHandle is called through the trap dispatcher to implement the PlotCIconHandle call. It allows
// a CIcon to be plotted, using the modifiers for an icon suite.
pascal OSErr __PlotCIconHandle(const Rect *theRect,
short align,
short transform,
CIconHandle theIcon)
{
ParamBlock theBlock;
char iconState;
// CIcon's don't exist on non-color machines.
if (!HaveCQD())
return paramErr;
// Since PlotCIconHandle is slower than PlotCIcon, we fall through to PlotCIcon when there is no alignment
// or transform. For visual consistancy, we set fore and back colors to black and white, because PlotDeep
// (See above) does that before plotting deep data.
if ((!align) && (!transform))
{
ColorSpec foreGround,
backGround;
SaveFore(&foreGround);
SaveBack(&backGround);
ForeColor(blackColor);
BackColor(whiteColor);
PlotCIcon(theRect, theIcon);
RestoreFore(&foreGround);
RestoreBack(&backGround);
return noErr;
}
// Lock down the CIcon while we plot it.
iconState = HGetState((Handle)theIcon);
HLock((Handle)theIcon);
// set up for call to SetupParamBlockForCIcon
theBlock.theGroup = aCIcon;
theBlock.userData = myGetHandleSize((Handle)theIcon); // Going into SetupParamBlockForCIcon, userData is the size of theIcon,
// coming out, it is the mask region.
theBlock.theRect = *theRect;
theBlock.userProc = (IconRetriever)(*theIcon);
theBlock.theTransform = transform & transformMask;
theBlock.theLabel = (transform & labelMask) >> labelShift;
theBlock.selected = transform & ttSelected;
theBlock.dontDraw = false;
SetupParamBlockForCIcon(&theBlock);
// do alignment, if any
if (align)
PerformAlignment(&theBlock, align);
// map the region to the size of the destination rect
AdjustRgn((RgnHandle)(theBlock.userData),
&(theBlock.maskMap.bounds),
&(theBlock.theRect));
// If we're plotting to a BitMap as opposed to a PixMap, we call PlotTheCIcon directly with a depth of 1.
// Otherwise, we need to to decide more carefully how to plot.
if (theBlock.dstMap->rowBytes < 0)
{
// If we're plotting to a printer or a PICT, we call PlotTheCIcon directly with with maximum depth.
// Otherwise, we do more checks.
if (theBlock.onScreen)
{
GlobalIconDataPtr theGlobals = GetGlobalPtr();
GDHandle theScreen;
// If we're assuming we're plotting to a given monitor, we call PlotTheCIcon directly for that
// monitor. Otherwise, we call DeviceLoop and let PlotTheCIcon decide for itself how to plot
// the data on monitor-by-monitor basis.
if (theScreen = theGlobals->virtualScreen)
PlotTheCIcon((*((*theScreen)->gdPMap))->pixelSize, 0, theScreen, &theBlock);
else
DeviceLoop((RgnHandle)(theBlock.userData),
(DeviceLoopDrawingProcPtr)PlotTheCIcon,
(long)(&theBlock), 0);
}
else
PlotTheCIcon(8, 0, NULL, &theBlock);
}
else
PlotTheCIcon(1, 1, NULL, &theBlock);
// restore the handle state of the CIconHandle
HSetState((Handle)theIcon, iconState);
// dispose of the mask region
DisposeRgn((RgnHandle)(theBlock.userData));
// dispose of the one-bit data handle we created in SetupParamBlockForCIcon
DisposHandle((Handle)(theBlock.theMask));
}
#endif