// This file has been modified from the IconFamily version .5 source distribution with the additon of the initWithBitmaps method. - Nathan Hamblen
// IconFamily.m
// IconFamily class implementation
// by Troy Stephens, Thomas Schnitzer, David Remahl, Nathan Day and Ben Haller
// version 0.5
//
// Project Home Page:
// http://homepage.mac.com/troy_stephens/software/objects/IconFamily/
//
// Problems, shortcomings, and uncertainties that I'm aware of are flagged
// with "NOTE:". Please address bug reports, bug fixes, suggestions, etc.
// to me at troy_stephens@mac.com
//
// This code is provided as-is, with no warranty, in the hope that it will be
// useful. However, it appears to work fine on Mac OS X 10.1.5 and 10.2. :-)
#import "IconFamily.h"
#import "NSString+CarbonFSSpecCreation.h"

@interface IconFamily (Internals)

+ (NSImage*) resampleImage:(NSImage*)image toIconWidth:(int)width usingImageInterpolation:(NSImageInterpolation)imageInterpolation;

+ (Handle) get32BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize;

+ (Handle) get8BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize;

+ (Handle) get8BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize;

+ (Handle) get1BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize;

- (BOOL) addResourceType:(OSType)type asResID:(int)resID;

@end

@implementation IconFamily

+ (IconFamily*) iconFamily
{
    return [[[IconFamily alloc] init] autorelease];
}

+ (IconFamily*) iconFamilyWithContentsOfFile:(NSString*)path
{
    return [[[IconFamily alloc] initWithContentsOfFile:path] autorelease];
}

+ (IconFamily*) iconFamilyWithIconOfFile:(NSString*)path
{
    return [[[IconFamily alloc] initWithIconOfFile:path] autorelease];
}

+ (IconFamily*) iconFamilyWithIconFamilyHandle:(IconFamilyHandle)hNewIconFamily
{
    return [[[IconFamily alloc] initWithIconFamilyHandle:hNewIconFamily] autorelease];
}

+ (IconFamily*) iconFamilyWithSystemIcon:(int)fourByteCode
{
    return [[[IconFamily alloc] initWithSystemIcon:fourByteCode] autorelease];
}

+ (IconFamily*) iconFamilyWithThumbnailsOfImage:(NSImage*)image
{
    return [[[IconFamily alloc] initWithThumbnailsOfImage:image] autorelease];
}

+ (IconFamily*) iconFamilyWithThumbnailsOfImage:(NSImage*)image usingImageInterpolation:(NSImageInterpolation)imageInterpolation
{
    return [[[IconFamily alloc] initWithThumbnailsOfImage:image usingImageInterpolation:imageInterpolation] autorelease];
}

// This is IconFamily's designated initializer. It creates a new IconFamily that initially has no elements.
//
// The proper way to do this is to simply allocate a zero-sized handle (not to be confused with an empty handle) and assign it to hIconFamily. This technique works on Mac OS X 10.2 as well as on 10.0.x and 10.1.x. Our previous technique of allocating an IconFamily struct with a resourceSize of 0 no longer works as of Mac OS X 10.2.
- init
{
    self = [super init];
    if (self) {
        hIconFamily = (IconFamilyHandle) NewHandle( 0 );
        if (hIconFamily == NULL) {
            [self autorelease];
            return nil;
        }
    }
    return self;
}

- initWithContentsOfFile:(NSString*)path
{
    FSSpec fsSpec;
    OSErr result;

    self = [self init];
    if (self) {
        if (hIconFamily) {
            DisposeHandle( (Handle)hIconFamily );
            hIconFamily = NULL;
        }
    if (![path getFSSpec:&fsSpec createFileIfNecessary:NO]) {
      [self autorelease];
      return nil;
    }
    result = ReadIconFile( &fsSpec, &hIconFamily );
    if (result != noErr) {
      [self autorelease];
      return nil;
    }
    }
    return self;
}

- initWithIconFamilyHandle:(IconFamilyHandle)hNewIconFamily
{
    self = [self init];
    if (self) {
        if (hIconFamily) {
            DisposeHandle( (Handle)hIconFamily );
            hIconFamily = NULL;
        }
        
// NOTE: Do we have to somehow "retain" the handle
        
// (increment its reference count)?
        hIconFamily = hNewIconFamily;
    }
    return self;
}

- initWithIconOfFile:(NSString*)path
{
    IconRef  iconRef;
    OSErr  result;
    SInt16  label;
    FSSpec  fileSpec;

    self = [self init];
    if (self)
    {
        if (hIconFamily)
        {
            DisposeHandle( (Handle)hIconFamily );
            hIconFamily = NULL;
        }

        if( ![path getFSSpec:&fileSpec createFileIfNecessary:NO] )
        {
            [self autorelease];
            return nil;
        }

        result = GetIconRefFromFile(
                                    &fileSpec,
                                    &iconRef,
                                    &label );

        if (result != noErr)
        {
            [self autorelease];
            return nil;
        }

        result = IconRefToIconFamily(
                                     iconRef,
                                     kSelectorAllAvailableData,
                                     &hIconFamily );

        if (result != noErr || !hIconFamily)
        {
            [self autorelease];
            return nil;
        }

        ReleaseIconRef( iconRef );
    }
    return self;
}

- initWithSystemIcon:(int)fourByteCode
{
    IconRef  iconRef;
    OSErr  result;

    self = [self init];
    if (self)
    {
        if (hIconFamily)
        {
            DisposeHandle( (Handle)hIconFamily );
            hIconFamily = NULL;
        }

        result = GetIconRef(kOnSystemDisk, kSystemIconsCreator, fourByteCode, &iconRef);

        if (result != noErr)
        {
            [self autorelease];
            return nil;
        }

        result = IconRefToIconFamily(
                                     iconRef,
                                     kSelectorAllAvailableData,
                                     &hIconFamily );

        if (result != noErr || !hIconFamily)
        {
            [self autorelease];
            return nil;
        }

        ReleaseIconRef( iconRef );
    }
    return self;
}

- initWithThumbnailsOfImage:(NSImage*)image
{
    
// The default is to use a high degree of antialiasing, producing a smooth image.
    return [self initWithThumbnailsOfImage:image usingImageInterpolation:NSImageInterpolationHigh];
}

- initWithThumbnailsOfImage:(NSImage*)image usingImageInterpolation:(NSImageInterpolation)imageInterpolation
{
    NSImage* iconImage128x128;
    NSImage* iconImage32x32;
    NSImage* iconImage16x16;
    NSBitmapImageRep* iconBitmap128x128;
    NSBitmapImageRep* iconBitmap32x32;
    NSBitmapImageRep* iconBitmap16x16;
    NSImage* bitmappedIconImage128x128;

    
// Start with a new, empty IconFamily.
    self = [self init];
    if (self == nil)
        return nil;

    
// Resample the given image to create a 128x128 pixel, 32-bit RGBA
    
// version, and use that as our "thumbnail" (128x128) icon and mask.
    
//
    
// Our +resampleImage:toIconWidth:... method, in its present form,
    
// returns an NSImage that contains an NSCacheImageRep, rather than
    
// an NSBitmapImageRep. We convert to an NSBitmapImageRep, so that
  
// our methods can scan the image data, using initWithFocusedViewRect:.
    iconImage128x128 = [IconFamily resampleImage:image toIconWidth:128 usingImageInterpolation:imageInterpolation];
  [iconImage128x128 lockFocus];
  iconBitmap128x128 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, 128, 128)];
  [iconImage128x128 unlockFocus];
    if (iconBitmap128x128) {
        [self setIconFamilyElement:kThumbnail32BitData fromBitmapImageRep:iconBitmap128x128];
        [self setIconFamilyElement:kThumbnail8BitMask  fromBitmapImageRep:iconBitmap128x128];
    }

    
// Create an NSImage with the iconBitmap128x128 NSBitmapImageRep, that we
    
// can resample to create the smaller icon family elements. (This is
    
// most likely more efficient than resampling from the original image again,
    
// particularly if it is large. It produces a slightly different result, but
    
// the difference is minor and should not be objectionable...)
    bitmappedIconImage128x128 = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
    [bitmappedIconImage128x128 addRepresentation:iconBitmap128x128];

    
// Resample the 128x128 image to create a 32x32 pixel, 32-bit RGBA version,
    
// and use that as our "large" (32x32) icon and 8-bit mask.
    iconImage32x32 = [IconFamily resampleImage:bitmappedIconImage128x128 toIconWidth:32 usingImageInterpolation:imageInterpolation];
  [iconImage32x32 lockFocus];
  iconBitmap32x32 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, 32, 32)];
  [iconImage32x32 unlockFocus];
    if (iconBitmap32x32) {
        [self setIconFamilyElement:kLarge32BitData fromBitmapImageRep:iconBitmap32x32];
        [self setIconFamilyElement:kLarge8BitData fromBitmapImageRep:iconBitmap32x32];
        [self setIconFamilyElement:kLarge8BitMask fromBitmapImageRep:iconBitmap32x32];
        [self setIconFamilyElement:kLarge1BitMask fromBitmapImageRep:iconBitmap32x32];
    }

    
// Resample the 128x128 image to create a 16x16 pixel, 32-bit RGBA version,
    
// and use that as our "small" (16x16) icon and 8-bit mask.
    iconImage16x16 = [IconFamily resampleImage:bitmappedIconImage128x128 toIconWidth:16 usingImageInterpolation:imageInterpolation];
  [iconImage16x16 lockFocus];
  iconBitmap16x16 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, 16, 16)];
  [iconImage16x16 unlockFocus];
    if (iconBitmap16x16) {
        [self setIconFamilyElement:kSmall32BitData fromBitmapImageRep:iconBitmap16x16];
        [self setIconFamilyElement:kSmall8BitData fromBitmapImageRep:iconBitmap16x16];
        [self setIconFamilyElement:kSmall8BitMask fromBitmapImageRep:iconBitmap16x16];
        [self setIconFamilyElement:kSmall1BitMask fromBitmapImageRep:iconBitmap16x16];
    }

    
// Release all of the images that we created and no longer need.
    [bitmappedIconImage128x128 release];
    [iconBitmap128x128 release];
    [iconBitmap32x32 release];
    [iconBitmap16x16 release];

    
// Return the new icon family!
    return self;
}

// Added by Nathan Hamblen for support of small icons that are not the same representation
// as large ones. The given bitmaps must have 128x128, 32x32, and 16x16 sizes.
- initWithBitmaps:(NSArray *)bitmaps
{
  NSEnumerator *reps = [bitmaps objectEnumerator];
  NSBitmapImageRep *currRep;

  unsigned long datakeys128[4] = {kThumbnail32BitData, kThumbnail8BitMask, (unsigned long)0, (unsigned long)0},
    datakeys32[4] = {kLarge32BitData, kLarge8BitData, kLarge8BitMask, kLarge1BitMask},
    datakeys16[4] = {kSmall32BitData, kSmall8BitData, kSmall8BitMask, kSmall1BitMask};
  unsigned long *datakeys;

  unsigned short i;

  
// Start with a new, empty IconFamily.
  self = [self init];
  if (self == nil)
    return nil;

  while (currRep = [reps nextObject])
  {
    switch ([currRep pixelsWide])
    {
      case 128:
        datakeys = datakeys128;
        break;
      case 32:
        datakeys = datakeys32;
        break;
      case 16:
        datakeys = datakeys16;
        break;
      default:
        continue;
    }
    for (i=0; i<4; i++)
      if (datakeys[i] != 0)
        [self setIconFamilyElement:datakeys[i] fromBitmapImageRep:currRep];
  }
  return self;
}

- (void) dealloc
{
    DisposeHandle( (Handle)hIconFamily );
    [super dealloc];
}

- (NSBitmapImageRep*) bitmapImageRepWithAlphaForIconFamilyElement:(OSType)elementType;
{
    NSBitmapImageRep* bitmapImageRep;
    int pixelsWide;
    Handle hRawBitmapData;
    Handle hRawMaskData;
    OSType maskElementType;
    OSErr result;
    unsigned long* pRawBitmapData;
    unsigned long* pRawBitmapDataEnd;
    unsigned char* pRawMaskData;
    unsigned char* pBitmapImageRepBitmapData;

    
// Make sure elementType is a valid type that we know how to handle, and
    
// figure out the dimensions and bit depth of the bitmap for that type.
    switch (elementType) {
  
// 'it32' 128x128 32-bit RGB image
  case kThumbnail32BitData:
    maskElementType = kThumbnail8BitMask;
    pixelsWide = 128;
      break;

  
// 'il32' 32x32 32-bit RGB image
  case kLarge32BitData:
    maskElementType = kLarge8BitMask;
    pixelsWide = 32;
      break;

  
// 'is32' 16x16 32-bit RGB image
  case kSmall32BitData:
    maskElementType = kSmall8BitMask;
    pixelsWide = 16;
      break;

  default:
      return nil;
    }

    
// Get the raw, uncompressed bitmap data for the requested element.
    hRawBitmapData = NewHandle( pixelsWide * pixelsWide * 4 );
    result = GetIconFamilyData( hIconFamily, elementType, hRawBitmapData );
    if (result != noErr) {
        DisposeHandle( hRawBitmapData );
        return nil;
    }

    
// Get the corresponding raw, uncompressed 8-bit mask data.
    hRawMaskData = NewHandle( pixelsWide * pixelsWide );
    result = GetIconFamilyData( hIconFamily, maskElementType, hRawMaskData );
    if (result != noErr) {
        DisposeHandle( hRawMaskData );
        hRawMaskData = NULL;
    }

    
// The retrieved raw bitmap data is stored at 32 bits per pixel: 3 bytes
    
// for the RGB color of each pixel, plus an extra unused byte. We can
    
// therefore fold the mask data into the color data in-place (though
    
// getting the proper byte ordering requires some bit-shifting).
    HLock( hRawBitmapData );
    pRawBitmapData = (unsigned long*) *hRawBitmapData;
    pRawBitmapDataEnd = pRawBitmapData + pixelsWide * pixelsWide;
    if (hRawMaskData) {
        HLock( hRawMaskData );
        pRawMaskData = *hRawMaskData;
        while (pRawBitmapData < pRawBitmapDataEnd)
            *pRawBitmapData++ = (*pRawBitmapData << 8) | *pRawMaskData++;
        HUnlock( hRawMaskData );
    } else {
        while (pRawBitmapData < pRawBitmapDataEnd)
            *pRawBitmapData++ = (*pRawBitmapData << 8) | 0xff;
    }

    
// Create a new NSBitmapImageRep with the given bitmap data. Note that
    
// when creating the NSBitmapImageRep we pass in NULL for the "planes"
    
// parameter. This causes the new NSBitmapImageRep to allocate its own
    
// buffer for the bitmap data (which it will own and release when the
    
// NSBitmapImageRep is released), rather than referencing the bitmap
    
// data we pass in (which will soon disappear when we call
    
// DisposeHandle() below!). (See the NSBitmapImageRep documentation for
    
// the -initWithBitmapDataPlanes:... method, where this is explained.)
    
//
    
// Once we have the new NSBitmapImageRep, we get a pointer to its
    
// bitmapData and copy our bitmap data in.
    bitmapImageRep = [[[NSBitmapImageRep alloc]
        initWithBitmapDataPlanes:NULL
                      pixelsWide:pixelsWide
                      pixelsHigh:pixelsWide
                   bitsPerSample:8
                 samplesPerPixel:4
                        hasAlpha:YES
                        isPlanar:NO
                  colorSpaceName:NSDeviceRGBColorSpace 
// NOTE: is this right?
                     bytesPerRow:0
                    bitsPerPixel:0] autorelease];
    pBitmapImageRepBitmapData = [bitmapImageRep bitmapData];
    if (pBitmapImageRepBitmapData) {
        memcpy( pBitmapImageRepBitmapData, *hRawBitmapData,
                pixelsWide * pixelsWide * 4 );
    }
    HUnlock( hRawBitmapData );

    
// Free the retrieved raw data.
    DisposeHandle( hRawBitmapData );
    if (hRawMaskData)
        DisposeHandle( hRawMaskData );

    
// Return nil if the NSBitmapImageRep didn't give us a buffer to copy into.
    if (pBitmapImageRepBitmapData == NULL)
        return nil;

    
// Return the new NSBitmapImageRep.
    return bitmapImageRep;
}

- (NSImage*) imageWithAllReps
{
    NSImage* image = NULL;
    image = [[[NSImage alloc] initWithData:[NSData dataWithBytes:*hIconFamily length:GetHandleSize((Handle)hIconFamily)]] autorelease];

    return image;

    
//investigate optimisations (dataWithBytesNoCopy:length: for example...)
}

- (BOOL) setIconFamilyElement:(OSType)elementType fromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep
{
    Handle hRawData = NULL;
    OSErr result;

    switch (elementType) {
  
// 'it32' 128x128 32-bit RGB image
  case kThumbnail32BitData:
      hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:128];
      break;

  
// 't8mk' 128x128 8-bit alpha mask
  case kThumbnail8BitMask:
      hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:128];
      break;

  
// 'il32' 32x32 32-bit RGB image
  case kLarge32BitData:
      hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:32];
      break;

  
// 'l8mk' 32x32 8-bit alpha mask
  case kLarge8BitMask:
      hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:32];
      break;

  
// 'ICN#' 32x32 1-bit alpha mask
  case kLarge1BitMask:
      hRawData = [IconFamily get1BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:32];
      break;

  
// 'icl8' 32x32 8-bit indexed image data
  case kLarge8BitData:
    hRawData = [IconFamily get8BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:32];
    break;

  
// 'is32' 16x16 32-bit RGB image
  case kSmall32BitData:
    hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:16];
    break;

  
// 's8mk' 16x16 8-bit alpha mask
  case kSmall8BitMask:
      hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:16];
      break;

  
// 'ics#' 16x16 1-bit alpha mask
  case kSmall1BitMask:
      hRawData = [IconFamily get1BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:16];
      break;

  
// 'ics8' 16x16 8-bit indexed image data
  case kSmall8BitData:
    hRawData = [IconFamily get8BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:16];
    break;

  default:
      return NO;
    }

  
// NSLog(@"setIconFamilyElement:%@ fromBitmapImageRep:%@ generated handle %p of size %d", NSFileTypeForHFSTypeCode(elementType), bitmapImageRep, hRawData, GetHandleSize(hRawData));
    if (hRawData == NULL)
  {
    NSLog(@"Null data returned to setIconFamilyElement:fromBitmapImageRep:");
    return NO;
  }

    result = SetIconFamilyData( hIconFamily, elementType, hRawData );
    DisposeHandle( hRawData );

    if (result != noErr)
  {
    NSLog(@"SetIconFamilyData() returned error %d", result);
    return NO;
  }

    return YES;
}

- (BOOL) setAsCustomIconForFile:(NSString*)path
{
    return( [self setAsCustomIconForFile:path withCompatibility:NO] );
}

- (BOOL) setAsCustomIconForFile:(NSString*)path withCompatibility:(BOOL)compat
{
    FSSpec targetFileFSSpec;
    FSRef targetFileFSRef;
    FSRef parentDirectoryFSRef;
    SInt16 file;
    OSErr result;
    FInfo finderInfo;
    Handle hExistingCustomIcon;
    Handle hIconFamilyCopy;
  NSDictionary *fileAttributes;
  OSType existingType = '????', existingCreator = '????';

    
// Get an FSRef and an FSSpec for the target file, and an FSRef for its parent directory that we can use in the FNNotify() call below.
    if (![path getFSRef:&targetFileFSRef createFileIfNecessary:NO])
    return NO;
    result = FSGetCatalogInfo( &targetFileFSRef, kFSCatInfoNone, NULL, NULL, &targetFileFSSpec, &parentDirectoryFSRef );
    if (result != noErr)
        return NO;

    
// Get the file's type and creator codes.
  fileAttributes = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:NO];
  if (fileAttributes)
  {
    existingType = [fileAttributes fileHFSTypeCode];
    existingCreator = [fileAttributes fileHFSCreatorCode];
    }

    
// Make sure the file has a resource fork that we can open. (Although
    
// this sounds like it would clobber an existing resource fork, the Carbon
    
// Resource Manager docs for this function say that's not the case. If
    
// the file already has a resource fork, we receive a result code of
    
// dupFNErr, which is not really an error per se, but just a notification
    
// to us that creating a new resource fork for the file was not necessary.)
    FSpCreateResFile( &targetFileFSSpec, existingCreator, existingType, smRoman );
    result = ResError();
    if (!(result == noErr || result == dupFNErr))
      return NO;

    
// Open the file's resource fork.
    file = FSpOpenResFile( &targetFileFSSpec, fsRdWrPerm );
    if (file == -1)
    return NO;

    
// Make a copy of the icon family data to pass to AddResource().
    
// (AddResource() takes ownership of the handle we pass in; after the
    
// CloseResFile() call its master pointer will be set to 0xffffffff.
    
// We want to keep the icon family data, so we make a copy.)
    
// HandToHand() returns the handle of the copy in hIconFamily.
    hIconFamilyCopy = (Handle) hIconFamily;
    result = HandToHand( &hIconFamilyCopy );
    if (result != noErr) {
        CloseResFile( file );
        return NO;
    }

    
// Remove the file's existing kCustomIconResource of type kIconFamilyType
    
// (if any).
    hExistingCustomIcon = GetResource( kIconFamilyType,  kCustomIconResource);
    if( hExistingCustomIcon )
        RemoveResource( hExistingCustomIcon );

    
// Now add our icon family as the file's new custom icon.
    AddResource( (Handle)hIconFamilyCopy, kIconFamilyType,
                 kCustomIconResource, "\p");

    if (ResError() != noErr) {
        CloseResFile( file );
        return NO;
    }

    if( compat )
    {
        [self addResourceType:kLarge8BitData asResID:kCustomIconResource];
        [self addResourceType:kLarge1BitMask asResID:kCustomIconResource];
        [self addResourceType:kSmall8BitData asResID:kCustomIconResource];
        [self addResourceType:kSmall1BitMask asResID:kCustomIconResource];
    }

    
// Close the file's resource fork, flushing the resource map and new icon
    
// data out to disk.
    CloseResFile( file );
    if (ResError() != noErr)
    return NO;

    
// Now we need to set the file's Finder info so the Finder will know that
    
// it has a custom icon. Start by getting the file's current finder info:
    result = FSpGetFInfo( &targetFileFSSpec, &finderInfo );
    if (result != noErr)
    return NO;

    
// Set the kHasCustomIcon flag, and clear the kHasBeenInited flag.
    
//
    
// From Apple's "CustomIcon" code sample:
    
// "set bit 10 (has custom icon) and unset the inited flag
    
// kHasBeenInited is 0x0100 so the mask will be 0xFEFF:"
    
// finderInfo.fdFlags = 0xFEFF & (finderInfo.fdFlags | kHasCustomIcon ) ;
    finderInfo.fdFlags = (finderInfo.fdFlags | kHasCustomIcon ) & ~kHasBeenInited;

    
// Now write the Finder info back.
    result = FSpSetFInfo( &targetFileFSSpec, &finderInfo );
    if (result != noErr)
    return NO;

    
// Notify the system that the directory containing the file has changed, to give Finder the chance to find out about the file's new custom icon.
    result = FNNotify( &parentDirectoryFSRef, kFNDirectoryModifiedMessage, kNilOptions );
    if (result != noErr)
        return NO;

    return YES;
}

+ (BOOL) removeCustomIconFromFile:(NSString*)path
{
    FSSpec targetFileFSSpec;
    FSRef targetFileFSRef;
    FSRef parentDirectoryFSRef;
    SInt16 file;
    OSErr result;
    FInfo finderInfo;
    Handle hExistingCustomIcon;

    
// Get an FSRef and an FSSpec for the target file, and an FSRef for its parent directory that we can use in the FNNotify() call below.
    if (![path getFSRef:&targetFileFSRef createFileIfNecessary:NO])
    return NO;
    result = FSGetCatalogInfo( &targetFileFSRef, kFSCatInfoNone, NULL, NULL, &targetFileFSSpec, &parentDirectoryFSRef );
    if (result != noErr)
        return NO;

    
// Open the file's resource fork, if it has one.
    file = FSpOpenResFile( &targetFileFSSpec, fsRdWrPerm );
    if (file == -1)
        return NO;

    
// Remove the file's existing kCustomIconResource of type kIconFamilyType
    
// (if any).
    hExistingCustomIcon = GetResource( kIconFamilyType, kCustomIconResource );
    if( hExistingCustomIcon )
        RemoveResource( hExistingCustomIcon );

    
// Close the file's resource fork, flushing the resource map out to disk.
    CloseResFile( file );
    if (ResError() != noErr)
        return NO;

    
// Now we need to set the file's Finder info so the Finder will know that
    
// it has no custom icon. Start by getting the file's current finder info:
    result = FSpGetFInfo( &targetFileFSSpec, &finderInfo );
    if (result != noErr)
        return NO;

    
// Clear the kHasCustomIcon flag and the kHasBeenInited flag.
    finderInfo.fdFlags = finderInfo.fdFlags & ~(kHasCustomIcon | kHasBeenInited);

    
// Now write the Finder info back.
    result = FSpSetFInfo( &targetFileFSSpec, &finderInfo );
    if (result != noErr)
        return NO;

    
// Notify the system that the directory containing the file has changed, to give Finder the chance to find out about the file's new custom icon.
    result = FNNotify( &parentDirectoryFSRef, kFNDirectoryModifiedMessage, kNilOptions );
    if (result != noErr)
        return NO;

    return YES;
}

- (BOOL) setAsCustomIconForDirectory:(NSString*)path
{
    return [self setAsCustomIconForDirectory:path withCompatibility:NO];
}

- (BOOL) setAsCustomIconForDirectory:(NSString*)path withCompatibility:(BOOL)compat
{
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL isDir;
    BOOL exists;
    NSString *iconrPath = [path stringByAppendingPathComponent:@"Icon\r"];
    FSSpec targetFileFSSpec, targetFolderFSSpec;
    FSRef targetFolderFSRef;
    SInt16 file;
    OSErr result;
    FInfo finderInfo;
    FSCatalogInfo catInfo;
    Handle hExistingCustomIcon;
    Handle hIconFamilyCopy;

    exists = [fm fileExistsAtPath:path isDirectory:&isDir];

    if( !isDir || !exists )
        return NO;

    if( [fm fileExistsAtPath:iconrPath] )
    {
        if( ![fm removeFileAtPath:iconrPath handler:nil] )
            return NO;
    }

    if (![iconrPath getFSSpec:&targetFileFSSpec createFileIfNecessary:YES])
        return NO;

    if( ![path getFSSpec:&targetFolderFSSpec createFileIfNecessary:YES] )
        return NO;

    if( ![path getFSRef:&targetFolderFSRef createFileIfNecessary:NO] )
        return NO;

    
// Make sure the file has a resource fork that we can open. (Although
    
// this sounds like it would clobber an existing resource fork, the Carbon
    
// Resource Manager docs for this function say that's not the case.)
    FSpCreateResFile( &targetFileFSSpec, '????', '????', smRoman );
    if (ResError() != noErr)
        return NO;

    
// Open the file's resource fork.
    file = FSpOpenResFile( &targetFileFSSpec, fsRdWrPerm );
    if (file == -1)
        return NO;

    
// Make a copy of the icon family data to pass to AddResource().
    
// (AddResource() takes ownership of the handle we pass in; after the
    
// CloseResFile() call its master pointer will be set to 0xffffffff.
    
// We want to keep the icon family data, so we make a copy.)
    
// HandToHand() returns the handle of the copy in hIconFamily.
    hIconFamilyCopy = (Handle) hIconFamily;
    result = HandToHand( &hIconFamilyCopy );
    if (result != noErr) {
        CloseResFile( file );
        return NO;
    }

    
// Remove the file's existing kCustomIconResource of type kIconFamilyType
    
// (if any).
    hExistingCustomIcon = GetResource( kIconFamilyType, kCustomIconResource );
    if( hExistingCustomIcon )
        RemoveResource( hExistingCustomIcon );

    
// Now add our icon family as the file's new custom icon.
    AddResource( (Handle)hIconFamilyCopy, kIconFamilyType,
                 kCustomIconResource, "\p");

    if (ResError() != noErr) {
        CloseResFile( file );
        return NO;
    }

    if( compat )
    {
        [self addResourceType:kLarge8BitData asResID:kCustomIconResource];
        [self addResourceType:kLarge1BitMask asResID:kCustomIconResource];
        [self addResourceType:kSmall8BitData asResID:kCustomIconResource];
        [self addResourceType:kSmall1BitMask asResID:kCustomIconResource];
    }

    
// Close the file's resource fork, flushing the resource map and new icon
    
// data out to disk.
    CloseResFile( file );
    if (ResError() != noErr)
        return NO;

    
// Make folder icon file invisible
    result = FSpGetFInfo( &targetFileFSSpec, &finderInfo );
    if (result != noErr)
        return NO;
    finderInfo.fdFlags = (finderInfo.fdFlags | kIsInvisible ) & ~kHasBeenInited;
    
// And write info back
    result = FSpSetFInfo( &targetFileFSSpec, &finderInfo );
    if (result != noErr)
        return NO;

    result = FSGetCatalogInfo( &targetFolderFSRef,
                               kFSCatInfoFinderInfo,
                               &catInfo, nil, nil, nil);
    if( result != noErr )
        return NO;

    ((DInfo*)catInfo.finderInfo)->frFlags = ( ((DInfo*)catInfo.finderInfo)->frFlags | kHasCustomIcon ) & ~kHasBeenInited;

    FSSetCatalogInfo( &targetFolderFSRef,
                      kFSCatInfoFinderInfo,
                      &catInfo);
    if( result != noErr )
        return NO;

    
// Notify the system that the target directory has changed, to give Finder the chance to find out about its new custom icon.
    result = FNNotify( &targetFolderFSRef, kFNDirectoryModifiedMessage, kNilOptions );
    if (result != noErr)
        return NO;

    return YES;
}

/*- (BOOL) writeToFile:(NSString*)path { FSSpec fsSpec; OSErr result; if (![path getFSSpec:&fsSpec createFileIfNecessary:YES]) return NO; result = WriteIconFile( hIconFamily, &fsSpec ); if (result != noErr) return NO; return YES; } This method has a problem with files not representable as an FSSpec.*/

- (BOOL) writeToFile:(NSString*)path
{
    NSData* iconData = NULL;

    HLock((Handle)hIconFamily);

    iconData = [NSData dataWithBytes:*hIconFamily length:GetHandleSize((Handle)hIconFamily)];
    [iconData writeToFile:path atomically:NO];

    HUnlock((Handle)hIconFamily);

    return YES;
}

@end

@implementation IconFamily (Internals)

+ (NSImage*) resampleImage:(NSImage*)image toIconWidth:(int)iconWidth usingImageInterpolation:(NSImageInterpolation)imageInterpolation
{
    NSGraphicsContext* graphicsContext;
    BOOL wasAntialiasing;
    NSImageInterpolation previousImageInterpolation;
    NSImage* newImage;
// NSBitmapImageRep* newBitmapImageRep;
// unsigned char* bitmapData;
// NSImageRep* originalImageRep;
    NSImage* workingImage;
    NSImageRep* workingImageRep;
    NSSize size, pixelSize, newSize;
    NSRect iconRect;
    NSRect targetRect;

    
// Create a working copy of the image and scale its size down to fit in
    
// the square area of the icon.
    
//
    
// It seems like there should be a more memory-efficient alternative to
    
// first duplicating the entire original image, but I don't know what it
    
// is. We need to change some properties ("size" and "scalesWhenResized")
    
// of the original image, but we shouldn't change the original, so a copy
    
// is necessary.
    workingImage = [image copyWithZone:[image zone]];
    [workingImage setScalesWhenResized:YES];
    size = [workingImage size];
    workingImageRep = [workingImage bestRepresentationForDevice:nil];
    if ([workingImageRep isKindOfClass:[NSBitmapImageRep class]]) {
        pixelSize.width  = [workingImageRep pixelsWide];
        pixelSize.height = [workingImageRep pixelsHigh];
        if (!NSEqualSizes( size, pixelSize )) {
            [workingImage setSize:pixelSize];
            [workingImageRep setSize:pixelSize];
            size = pixelSize;
        }
    }
    if (size.width >= size.height) {
        newSize.width  = iconWidth;
        newSize.height = floor( (float) iconWidth * size.height / size.width + 0.5 );
    } else {
        newSize.height = iconWidth;
        newSize.width  = floor( (float) iconWidth * size.width / size.height + 0.5 );
    }
    [workingImage setSize:newSize];

#if 1   
// This is the way that works. It gives the newImage an NSCachedImageRep.

    
// Create a new image the size of the icon, and clear it to transparent.
    newImage = [[NSImage alloc] initWithSize:NSMakeSize(iconWidth,iconWidth)];
    [newImage lockFocus];
    iconRect.origin.x = iconRect.origin.y = 0;
    iconRect.size.width = iconRect.size.height = iconWidth;
    [[NSColor clearColor] set];
    NSRectFill( iconRect );

    
// Set current graphics context to use antialiasing and high-quality
    
// image scaling.
    graphicsContext = [NSGraphicsContext currentContext];
    wasAntialiasing = [graphicsContext shouldAntialias];
    previousImageInterpolation = [graphicsContext imageInterpolation];
    [graphicsContext setShouldAntialias:YES];
    [graphicsContext setImageInterpolation:imageInterpolation];

    
// Composite the working image into the icon bitmap, centered.
    targetRect.origin.x = ((float)iconWidth - newSize.width ) / 2.0;
    targetRect.origin.y = ((float)iconWidth - newSize.height) / 2.0;
    targetRect.size.width = newSize.width;
    targetRect.size.height = newSize.height;
    [workingImageRep drawInRect:targetRect];

    
// Restore previous graphics context settings.
    [graphicsContext setShouldAntialias:wasAntialiasing];
    [graphicsContext setImageInterpolation:previousImageInterpolation];

    [newImage unlockFocus];

    [workingImage release];

#else   
// This was an attempt at explicitly giving the NSImage an NSBitmapImageRep
        
// and drawing to that NSBitmapImageRep. It doesn't work. (See comments
        
// in -initWithThumbnailsOfImage:)
// // Create a new 32-bit RGBA bitmap that is width x width pixels.
    originalImageRep = [image bestRepresentationForDevice:nil];
    newImage = [[NSImage alloc] initWithSize:NSMakeSize(iconWidth,iconWidth)];
    [newImage setDataRetained:YES];
// [newImage setCachedSeparately:YES];
    newBitmapImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
  pixelsWide:iconWidth
  pixelsHigh:iconWidth
// bitsPerSample:8
// samplesPerPixel:4
  bitsPerSample:[originalImageRep bitsPerSample]
  samplesPerPixel:[(NSBitmapImageRep*)originalImageRep samplesPerPixel]
  hasAlpha:[originalImageRep hasAlpha]
  isPlanar:NO
  colorSpaceName:[originalImageRep colorSpaceName]
  bytesPerRow:0
  bitsPerPixel:0];
    [newImage addRepresentation:newBitmapImageRep];
    [newImage setScalesWhenResized:YES];
    [newBitmapImageRep release];
// bitmapData = [newBitmapImageRep bitmapData];
// if (bitmapData)
// memset( bitmapData, 128, iconWidth * iconWidth * 4 );
    
// Copy the original image into the new bitmap, rescaling it to fit.
// [newImage lockFocus];
    [newImage lockFocusOnRepresentation:newBitmapImageRep];
// [image compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
// iconRect.origin.x = iconRect.origin.y = 0;
// iconRect.size.width = iconRect.size.height = iconWidth;
// [[NSColor clearColor] set];
// NSRectFill( iconRect );
    [workingImage compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
    [newImage unlockFocus];

    [workingImage release];
#endif

    
// Return the new image!
    return [newImage autorelease];
}

+ (Handle) get32BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize
{
    Handle hRawData;
    unsigned char* pRawData;
    Size rawDataSize;
    unsigned char* pSrc;
    unsigned char* pDest;
    int x, y;
    unsigned char alphaByte;
    float oneOverAlpha;

    
// Get information about the bitmapImageRep.
    int pixelsWide      = [bitmapImageRep pixelsWide];
    int pixelsHigh      = [bitmapImageRep pixelsHigh];
    int bitsPerSample   = [bitmapImageRep bitsPerSample];
    int samplesPerPixel = [bitmapImageRep samplesPerPixel];
    int bitsPerPixel    = [bitmapImageRep bitsPerPixel];
// BOOL hasAlpha = [bitmapImageRep hasAlpha];
    BOOL isPlanar       = [bitmapImageRep isPlanar];
// int numberOfPlanes = [bitmapImageRep numberOfPlanes];
    int bytesPerRow     = [bitmapImageRep bytesPerRow];
// int bytesPerPlane = [bitmapImageRep bytesPerPlane];
    unsigned char* bitmapData = [bitmapImageRep bitmapData];

    
// Make sure bitmap has the required dimensions.
    if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize)
  return NULL;

    
// So far, this code only handles non-planar 32-bit RGBA and 24-bit RGB source bitmaps.
    
// This could be made more flexible with some additional programming to accommodate other possible
    
// formats...
    if (isPlanar)
  {
    NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES");
    return NULL;
  }
    if (bitsPerSample != 8)
  {
    NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d", bitsPerSample);
    return NULL;
  }

  if (((samplesPerPixel == 3) && (bitsPerPixel == 24)) || ((samplesPerPixel == 4) && (bitsPerPixel == 32)))
  {
    rawDataSize = pixelsWide * pixelsHigh * 4;
    hRawData = NewHandle( rawDataSize );
    if (hRawData == NULL)
      return NULL;
    pRawData = *hRawData;

    pSrc = bitmapData;
    pDest = pRawData;

    if (bitsPerPixel == 32) {
      for (y = 0; y < pixelsHigh; y++) {
        pSrc = bitmapData + y * bytesPerRow;
          for (x = 0; x < pixelsWide; x++) {
            
// Each pixel is 3 bytes of RGB data, followed by 1 byte of
            
// alpha. The RGB values are premultiplied by the alpha (so
            
// that Quartz can save time when compositing the bitmap to a
            
// destination), and we undo this premultiplication (with some
            
// lossiness unfortunately) when retrieving the bitmap data.
            *pDest++ = alphaByte = *(pSrc+3);
            if (alphaByte) {
              oneOverAlpha = 255.0f / (float)alphaByte;
              *pDest++ = *(pSrc+0) * oneOverAlpha;
              *pDest++ = *(pSrc+1) * oneOverAlpha;
              *pDest++ = *(pSrc+2) * oneOverAlpha;
            } else {
              *pDest++ = 0;
              *pDest++ = 0;
              *pDest++ = 0;
            }
            pSrc+=4;
        }
      }
    } else if (bitsPerPixel == 24) {
      for (y = 0; y < pixelsHigh; y++) {
        pSrc = bitmapData + y * bytesPerRow;
        for (x = 0; x < pixelsWide; x++) {
          *pDest++ = 0;
          *pDest++ = *pSrc++;
          *pDest++ = *pSrc++;
          *pDest++ = *pSrc++;
        }
      }
    }
  }
  else
  {
    NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %", samplesPerPixel, bitsPerPixel);
    return NULL;
  }

    return hRawData;
}

+ (Handle) get8BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize
{
    Handle hRawData;
    unsigned char* pRawData;
    Size rawDataSize;
    unsigned char* pSrc;
    unsigned char* pDest;
    int x, y;

    
// Get information about the bitmapImageRep.
    int pixelsWide      = [bitmapImageRep pixelsWide];
    int pixelsHigh      = [bitmapImageRep pixelsHigh];
    int bitsPerSample   = [bitmapImageRep bitsPerSample];
    int samplesPerPixel = [bitmapImageRep samplesPerPixel];
    int bitsPerPixel    = [bitmapImageRep bitsPerPixel];
    BOOL isPlanar       = [bitmapImageRep isPlanar];
    int bytesPerRow     = [bitmapImageRep bytesPerRow];
    unsigned char* bitmapData = [bitmapImageRep bitmapData];

    
// Make sure bitmap has the required dimensions.
    if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize)
        return NULL;

    
// So far, this code only handles non-planar 32-bit RGBA and 24-bit RGB source bitmaps.
    
// This could be made more flexible with some additional programming...
    if (isPlanar)
  {
    NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES");
    return NULL;
  }
    if (bitsPerSample != 8)
  {
    NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d", bitsPerSample);
    return NULL;
  }

  if (((samplesPerPixel == 3) && (bitsPerPixel == 24)) || ((samplesPerPixel == 4) && (bitsPerPixel == 32)))
  {
    CGDirectPaletteRef cgPal;
    CGDeviceColor cgCol;

    rawDataSize = pixelsWide * pixelsHigh;
    hRawData = NewHandle( rawDataSize );
    if (hRawData == NULL)
      return NULL;
    pRawData = *hRawData;

    cgPal = CGPaletteCreateDefaultColorPalette();

    pSrc = bitmapData;
    pDest = pRawData;
    if (bitsPerPixel == 32) {
      for (y = 0; y < pixelsHigh; y++) {
        pSrc = bitmapData + y * bytesPerRow;
        for (x = 0; x < pixelsWide; x++) {
          cgCol.red = ((float)*(pSrc)) / 255;
          cgCol.green = ((float)*(pSrc+1)) / 255;
          cgCol.blue = ((float)*(pSrc+2)) / 255;

          *pDest++ = CGPaletteGetIndexForColor(cgPal, cgCol);

          pSrc+=4;
        }
      }
    } else if (bitsPerPixel == 24) {
      for (y = 0; y < pixelsHigh; y++) {
        pSrc = bitmapData + y * bytesPerRow;
        for (x = 0; x < pixelsWide; x++) {
          cgCol.red = ((float)*(pSrc)) / 255;
          cgCol.green = ((float)*(pSrc+1)) / 255;
          cgCol.blue = ((float)*(pSrc+2)) / 255;

          *pDest++ = CGPaletteGetIndexForColor(cgPal, cgCol);

          pSrc+=3;
        }
      }
    }

    CGPaletteRelease(cgPal);
  }
  else
  {
    NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %", samplesPerPixel, bitsPerPixel);
    return NULL;
  }

    return hRawData;
}

+ (Handle) get8BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize
{
    Handle hRawData;
    unsigned char* pRawData;
    Size rawDataSize;
    unsigned char* pSrc;
    unsigned char* pDest;
    int x, y;

    
// Get information about the bitmapImageRep.
    int pixelsWide      = [bitmapImageRep pixelsWide];
    int pixelsHigh      = [bitmapImageRep pixelsHigh];
    int bitsPerSample   = [bitmapImageRep bitsPerSample];
    int samplesPerPixel = [bitmapImageRep samplesPerPixel];
    int bitsPerPixel    = [bitmapImageRep bitsPerPixel];
// BOOL hasAlpha = [bitmapImageRep hasAlpha];
    BOOL isPlanar       = [bitmapImageRep isPlanar];
// int numberOfPlanes = [bitmapImageRep numberOfPlanes];
    int bytesPerRow     = [bitmapImageRep bytesPerRow];
// int bytesPerPlane = [bitmapImageRep bytesPerPlane];
    unsigned char* bitmapData = [bitmapImageRep bitmapData];

    
// Make sure bitmap has the required dimensions.
    if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize)
    return NULL;

    
// So far, this code only handles non-planar 32-bit RGBA, 24-bit RGB and 8-bit grayscale source bitmaps.
    
// This could be made more flexible with some additional programming...
    if (isPlanar)
  {
    NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES");
    return NULL;
  }
    if (bitsPerSample != 8)
  {
    NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d", bitsPerSample);
    return NULL;
  }

  if (((samplesPerPixel == 1) && (bitsPerPixel == 8)) || ((samplesPerPixel == 3) && (bitsPerPixel == 24)) || ((samplesPerPixel == 4) && (bitsPerPixel == 32)))
  {
    rawDataSize = pixelsWide * pixelsHigh;
    hRawData = NewHandle( rawDataSize );
    if (hRawData == NULL)
      return NULL;
    pRawData = *hRawData;

    pSrc = bitmapData;
    pDest = pRawData;

    if (bitsPerPixel == 32) {
      for (y = 0; y < pixelsHigh; y++) {
        pSrc = bitmapData + y * bytesPerRow;
        for (x = 0; x < pixelsWide; x++) {
          pSrc += 3;
          *pDest++ = *pSrc++;
        }
      }
    }
    else if (bitsPerPixel == 24) {
      memset( pDest, 255, rawDataSize );
    }
    else if (bitsPerPixel == 8) {
      for (y = 0; y < pixelsHigh; y++) {
        memcpy( pDest, pSrc, pixelsWide );
        pSrc += bytesPerRow;
        pDest += pixelsWide;
      }
    }
  }
  else
  {
    NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %", samplesPerPixel, bitsPerPixel);
    return NULL;
  }

    return hRawData;
}

// NOTE: This method hasn't been fully tested yet.
+ (Handle) get1BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize
{
    Handle hRawData;
    unsigned char* pRawData;
    Size rawDataSize;
    unsigned char* pSrc;
    unsigned char* pDest;
    int x, y;
    unsigned char maskByte;

    
// Get information about the bitmapImageRep.
    int pixelsWide      = [bitmapImageRep pixelsWide];
    int pixelsHigh      = [bitmapImageRep pixelsHigh];
    int bitsPerSample   = [bitmapImageRep bitsPerSample];
    int samplesPerPixel = [bitmapImageRep samplesPerPixel];
    int bitsPerPixel    = [bitmapImageRep bitsPerPixel];
// BOOL hasAlpha = [bitmapImageRep hasAlpha];
    BOOL isPlanar       = [bitmapImageRep isPlanar];
// int numberOfPlanes = [bitmapImageRep numberOfPlanes];
    int bytesPerRow     = [bitmapImageRep bytesPerRow];
// int bytesPerPlane = [bitmapImageRep bytesPerPlane];
    unsigned char* bitmapData = [bitmapImageRep bitmapData];

    
// Make sure bitmap has the required dimensions.
    if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize)
    return NULL;

    
// So far, this code only handles non-planar 32-bit RGBA, 24-bit RGB, 8-bit grayscale, and 1-bit source bitmaps.
    
// This could be made more flexible with some additional programming...
    if (isPlanar)
  {
    NSLog(@"get1BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES");
    return NULL;
  }

  if (((bitsPerPixel == 1) && (samplesPerPixel == 1) && (bitsPerSample == 1)) || ((bitsPerPixel == 8) && (samplesPerPixel == 1) && (bitsPerSample == 8)) ||
    ((bitsPerPixel == 24) && (samplesPerPixel == 3) && (bitsPerSample == 8)) || ((bitsPerPixel == 32) && (samplesPerPixel == 4) && (bitsPerSample == 8)))
  {
    rawDataSize = (pixelsWide * pixelsHigh)/4;
    hRawData = NewHandle( rawDataSize );
    if (hRawData == NULL)
      return NULL;
    pRawData = *hRawData;

    pSrc = bitmapData;
    pDest = pRawData;

    if (bitsPerPixel == 32) {
      for (y = 0; y < pixelsHigh; y++) {
        pSrc = bitmapData + y * bytesPerRow;
        for (x = 0; x < pixelsWide; x += 8) {
          maskByte = 0;
          maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x80 : 0; pSrc += 4;
          maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x40 : 0; pSrc += 4;
          maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x20 : 0; pSrc += 4;
          maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x10 : 0; pSrc += 4;
          maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x08 : 0; pSrc += 4;
          maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x04 : 0; pSrc += 4;
          maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x02 : 0; pSrc += 4;
          maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x01 : 0; pSrc += 4;
          *pDest++ = maskByte;
        }
      }
    }
    else if (bitsPerPixel == 24) {
      memset( pDest, 255, rawDataSize );
    }
    else if (bitsPerPixel == 8) {
      for (y = 0; y < pixelsHigh; y++) {
        pSrc = bitmapData + y * bytesPerRow;
        for (x = 0; x < pixelsWide; x += 8) {
          maskByte = 0;
          maskByte |= *pSrc++ ? 0x80 : 0;
          maskByte |= *pSrc++ ? 0x40 : 0;
          maskByte |= *pSrc++ ? 0x20 : 0;
          maskByte |= *pSrc++ ? 0x10 : 0;
          maskByte |= *pSrc++ ? 0x08 : 0;
          maskByte |= *pSrc++ ? 0x04 : 0;
          maskByte |= *pSrc++ ? 0x02 : 0;
          maskByte |= *pSrc++ ? 0x01 : 0;
          *pDest++ = maskByte;
        }
      }
    }
    else if (bitsPerPixel == 1) {
      for (y = 0; y < pixelsHigh; y++) {
        memcpy( pDest, pSrc, pixelsWide / 8 );
        pDest += pixelsWide / 8;
        pSrc += bytesPerRow;
      }
    }

    memcpy( pRawData+(pixelsWide*pixelsHigh)/8, pRawData, (pixelsWide*pixelsHigh)/8 );
  }
  else
  {
    NSLog(@"get1BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerPixel == %d, samplesPerPixel== %d, bitsPerSample == %d", bitsPerPixel, samplesPerPixel, bitsPerSample);
    return NULL;
  }

    return hRawData;
}

- (BOOL) addResourceType:(OSType)type asResID:(int)resID
{
    Handle hIconRes = NewHandle(0);
    OSErr err;

    err = GetIconFamilyData( hIconFamily, type, hIconRes );

    if( !GetHandleSize(hIconRes) || err != noErr )
        return NO;

    AddResource( hIconRes, type, resID, "\p" );

    return YES;
}

@end

// Methods for interfacing with the Carbon Scrap Manager (analogous to and
// interoperable with the Cocoa Pasteboard).
@implementation IconFamily (ScrapAdditions)

+ (BOOL) canInitWithScrap
{
    ScrapRef scrap = NULL;
    ScrapFlavorInfo* scrapInfos = NULL;
    UInt32 numInfos = 0;
    int i = 0;
    BOOL canInit = NO;

    GetCurrentScrap(&scrap);

    GetScrapFlavorCount(scrap,&numInfos);
    scrapInfos = malloc( sizeof(ScrapFlavorInfo)*numInfos );

    GetScrapFlavorInfoList(scrap, &numInfos, scrapInfos);

    for( i=0; i<numInfos; i++ )
    {
        if( scrapInfos[i].flavorType == 'icns' )
            canInit = YES;
    }

    free( scrapInfos );

    return canInit;
}

+ (IconFamily*) iconFamilyWithScrap
{
    return [[[IconFamily alloc] initWithScrap] autorelease];
}

- initWithScrap
{
    Handle storageMem = NULL;
    Size amountMem = 0;
    ScrapRef scrap;

    self = [super init];

    if( self )
    {
        GetCurrentScrap(&scrap);

        GetScrapFlavorSize( scrap, 'icns', &amountMem );

        storageMem = NewHandle(amountMem);

        GetScrapFlavorData( scrap, 'icns', &amountMem, *storageMem );

        hIconFamily = (IconFamilyHandle)storageMem;
    }
    return self;
}

- (BOOL) putOnScrap
{
    ScrapRef scrap = NULL;

    ClearCurrentScrap();
    GetCurrentScrap(&scrap);

    HLock((Handle)hIconFamily);
    PutScrapFlavor( scrap, 'icns', kScrapFlavorMaskNone, GetHandleSize((Handle)hIconFamily), *hIconFamily);
    HUnlock((Handle)hIconFamily);
    return YES;
}

@end