// IconFamily class implementation // by Troy Stephens, Thomas Schnitzer, David Remahl, Nathan Day and Ben Haller // 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. :-) //IconFamily depends heavily upon Carbon calls and thus will only work in Mac OS X #ifdef MAC_OS_X_VERSION_10_0 #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 ; @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. if (( self = [ super init ])) hIconFamily = ( IconFamilyHandle ) NewHandle ( 0 ); if ( hIconFamily == NULL ) { - initWithContentsOfFile: ( NSString * ) path DisposeHandle ( ( Handle ) hIconFamily ); if ( ! [ path getFSSpec :& fsSpec createFileIfNecessary : NO ]) { result = ReadIconFile ( & fsSpec , & hIconFamily ); - initWithIconFamilyHandle: ( IconFamilyHandle ) hNewIconFamily DisposeHandle ( ( Handle ) hIconFamily ); // NOTE: Do we have to somehow "retain" the handle // (increment its reference count)? hIconFamily = hNewIconFamily ; - initWithIconOfFile: ( NSString * ) path DisposeHandle ( ( Handle ) hIconFamily ); if ( ! [ path getFSSpec :& fileSpec createFileIfNecessary : NO ] ) result = GetIconRefFromFile ( result = IconRefToIconFamily ( kSelectorAllAvailableData , if ( result != noErr || ! hIconFamily ) ReleaseIconRef ( iconRef ); - initWithSystemIcon: ( int ) fourByteCode DisposeHandle ( ( Handle ) hIconFamily ); result = GetIconRef ( kOnSystemDisk , kSystemIconsCreator , fourByteCode , & iconRef ); result = IconRefToIconFamily ( kSelectorAllAvailableData , if ( result != noErr || ! hIconFamily ) ReleaseIconRef ( iconRef ); - 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 ; NSBitmapImageRep * iconBitmap128x128 ; NSBitmapImageRep * iconBitmap32x32 ; NSBitmapImageRep * iconBitmap16x16 ; NSImage * bitmappedIconImage128x128 ; // Start with a new, empty IconFamily. // 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 ]; [ 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 ]; [ 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 ]; [ 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! DisposeHandle ( ( Handle ) hIconFamily ); - ( NSBitmapImageRep * ) bitmapImageRepWithAlphaForIconFamilyElement: ( OSType ) elementType ; NSBitmapImageRep * bitmapImageRep ; unsigned long * pRawBitmapData ; unsigned long * pRawBitmapDataEnd ; 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. // 'it32' 128x128 32-bit RGB image case kThumbnail32BitData : maskElementType = kThumbnail8BitMask ; // 'il32' 32x32 32-bit RGB image maskElementType = kLarge8BitMask ; // 'is32' 16x16 32-bit RGB image maskElementType = kSmall8BitMask ; // Get the raw, uncompressed bitmap data for the requested element. hRawBitmapData = NewHandle ( pixelsWide * pixelsWide * 4 ); result = GetIconFamilyData ( hIconFamily , elementType , hRawBitmapData ); DisposeHandle ( hRawBitmapData ); // Get the corresponding raw, uncompressed 8-bit mask data. hRawMaskData = NewHandle ( pixelsWide * pixelsWide ); result = GetIconFamilyData ( hIconFamily , maskElementType , hRawMaskData ); DisposeHandle ( hRawMaskData ); // 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). pRawBitmapData = ( unsigned long * ) * hRawBitmapData ; pRawBitmapDataEnd = pRawBitmapData + pixelsWide * pixelsWide ; pRawMaskData = * hRawMaskData ; while ( pRawBitmapData < pRawBitmapDataEnd ) { * pRawBitmapData = ( * pRawBitmapData << 8 ) | * pRawMaskData ++ ; 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 colorSpaceName : NSDeviceRGBColorSpace // NOTE: is this right? bitsPerPixel : 0 ] autorelease ]; pBitmapImageRepBitmapData = [ bitmapImageRep bitmapData ]; if ( pBitmapImageRepBitmapData ) { memcpy ( pBitmapImageRepBitmapData , * hRawBitmapData , pixelsWide * pixelsWide * 4 ); HUnlock ( hRawBitmapData ); // Free the retrieved raw data. DisposeHandle ( hRawBitmapData ); DisposeHandle ( hRawMaskData ); // Return nil if the NSBitmapImageRep didn't give us a buffer to copy into. if ( pBitmapImageRepBitmapData == NULL ) // Return the new NSBitmapImageRep. - ( NSImage * ) imageWithAllReps image = [[[ NSImage alloc ] initWithData : [ NSData dataWithBytes :* hIconFamily length : GetHandleSize (( Handle ) hIconFamily )]] autorelease ]; //investigate optimisations (dataWithBytesNoCopy:length: for example...) - ( BOOL ) setIconFamilyElement : ( OSType ) elementType fromBitmapImageRep : ( NSBitmapImageRep * ) bitmapImageRep // 'it32' 128x128 32-bit RGB image case kThumbnail32BitData : hRawData = [ IconFamily get32BitDataFromBitmapImageRep : bitmapImageRep requiredPixelSize : 128 ]; // 't8mk' 128x128 8-bit alpha mask hRawData = [ IconFamily get8BitMaskFromBitmapImageRep : bitmapImageRep requiredPixelSize : 128 ]; // 'il32' 32x32 32-bit RGB image hRawData = [ IconFamily get32BitDataFromBitmapImageRep : bitmapImageRep requiredPixelSize : 32 ]; // 'l8mk' 32x32 8-bit alpha mask hRawData = [ IconFamily get8BitMaskFromBitmapImageRep : bitmapImageRep requiredPixelSize : 32 ]; // 'ICN#' 32x32 1-bit alpha mask hRawData = [ IconFamily get1BitMaskFromBitmapImageRep : bitmapImageRep requiredPixelSize : 32 ]; // 'icl8' 32x32 8-bit indexed image data hRawData = [ IconFamily get8BitDataFromBitmapImageRep : bitmapImageRep requiredPixelSize : 32 ]; // 'is32' 16x16 32-bit RGB image hRawData = [ IconFamily get32BitDataFromBitmapImageRep : bitmapImageRep requiredPixelSize : 16 ]; // 's8mk' 16x16 8-bit alpha mask hRawData = [ IconFamily get8BitMaskFromBitmapImageRep : bitmapImageRep requiredPixelSize : 16 ]; // 'ics#' 16x16 1-bit alpha mask hRawData = [ IconFamily get1BitMaskFromBitmapImageRep : bitmapImageRep requiredPixelSize : 16 ]; // 'ics8' 16x16 8-bit indexed image data hRawData = [ IconFamily get8BitDataFromBitmapImageRep : bitmapImageRep requiredPixelSize : 16 ]; NSLog ( @"Null data returned to setIconFamilyElement:fromBitmapImageRep:" ); result = SetIconFamilyData ( hIconFamily , elementType , hRawData ); DisposeHandle ( hRawData ); NSLog ( @"SetIconFamilyData() returned error %d" , result ); - ( BOOL ) setAsCustomIconForFile : ( NSString * ) path return [ self setAsCustomIconForFile : path withCompatibility : NO ] ; - ( BOOL ) setAsCustomIconForFile : ( NSString * ) path withCompatibility : ( BOOL ) compat FSRef parentDirectoryFSRef ; Handle hExistingCustomIcon ; NSDictionary * fileAttributes ; OSType existingType = kUnknownType , existingCreator = kUnknownType ; // 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 ]) result = FSGetCatalogInfo ( & targetFileFSRef , kFSCatInfoNone , NULL , NULL , & targetFileFSSpec , & parentDirectoryFSRef ); // Get the file's type and creator codes. fileAttributes = [[ NSFileManager defaultManager ] fileAttributesAtPath : path traverseLink : NO ]; 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 ); if ( ! ( result == noErr || result == dupFNErr )) // Open the file's resource fork. file = FSpOpenResFile ( & targetFileFSSpec , fsRdWrPerm ); // 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 ); // Remove the file's existing kCustomIconResource of type kIconFamilyType 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 ) { [ 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 // 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 ); // 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 ); // 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 ); + ( BOOL ) removeCustomIconFromFile : ( NSString * ) path FSRef parentDirectoryFSRef ; 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 ]) result = FSGetCatalogInfo ( & targetFileFSRef , kFSCatInfoNone , NULL , NULL , & targetFileFSSpec , & parentDirectoryFSRef ); // Open the file's resource fork, if it has one. file = FSpOpenResFile ( & targetFileFSSpec , fsRdWrPerm ); // Remove the file's existing kCustomIconResource of type kIconFamilyType hExistingCustomIcon = GetResource ( kIconFamilyType , kCustomIconResource ); if ( hExistingCustomIcon ) RemoveResource ( hExistingCustomIcon ); // Close the file's resource fork, flushing the resource map out to disk. // 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 ); // Clear the kHasCustomIcon flag and the kHasBeenInited flag. finderInfo . fdFlags = finderInfo . fdFlags & ~ ( kHasCustomIcon | kHasBeenInited ); // Now write the Finder info back. result = FSpSetFInfo ( & targetFileFSSpec , & finderInfo ); // 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 ); - ( BOOL ) setAsCustomIconForDirectory : ( NSString * ) path return [ self setAsCustomIconForDirectory : path withCompatibility : NO ]; - ( BOOL ) setAsCustomIconForDirectory : ( NSString * ) path withCompatibility : ( BOOL ) compat NSFileManager * fm = [ NSFileManager defaultManager ]; NSString * iconrPath = [ path stringByAppendingPathComponent : @"Icon \r " ]; FSSpec targetFileFSSpec , targetFolderFSSpec ; Handle hExistingCustomIcon ; exists = [ fm fileExistsAtPath : path isDirectory :& isDir ]; if ( [ fm fileExistsAtPath : iconrPath ] ) if ( ! [ fm removeFileAtPath : iconrPath handler : nil ] ) if ( ! [ iconrPath getFSSpec :& targetFileFSSpec createFileIfNecessary : YES ]) if ( ! [ path getFSSpec :& targetFolderFSSpec createFileIfNecessary : YES ] ) if ( ! [ path getFSRef :& targetFolderFSRef createFileIfNecessary : 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 , kUnknownType , kUnknownType , smRoman ); // Open the file's resource fork. file = FSpOpenResFile ( & targetFileFSSpec , fsRdWrPerm ); // 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 ); // Remove the file's existing kCustomIconResource of type kIconFamilyType 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 ) { [ 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 // Make folder icon file invisible result = FSpGetFInfo ( & targetFileFSSpec , & finderInfo ); finderInfo . fdFlags = ( finderInfo . fdFlags | kIsInvisible ) & ~ kHasBeenInited ; result = FSpSetFInfo ( & targetFileFSSpec , & finderInfo ); result = FSGetCatalogInfo ( & targetFolderFSRef , & catInfo , nil , nil , nil ); (( DInfo * ) catInfo . finderInfo ) -> frFlags = ( (( DInfo * ) catInfo . finderInfo ) -> frFlags | kHasCustomIcon ) & ~ kHasBeenInited ; FSSetCatalogInfo ( & targetFolderFSRef , // 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 ); /*- (BOOL) writeToFile:(NSString*)path if (![path getFSSpec:&fsSpec createFileIfNecessary:YES]) result = WriteIconFile( hIconFamily, &fsSpec ); } This method has a problem with files not representable as an FSSpec.*/ - ( BOOL ) writeToFile : ( NSString * ) path HLock (( Handle ) hIconFamily ); iconData = [ NSData dataWithBytes :* hIconFamily length : GetHandleSize (( Handle ) hIconFamily )]; [ iconData writeToFile : path atomically : NO ]; HUnlock (( Handle ) hIconFamily ); @implementation IconFamily (Internals) + ( NSImage * ) resampleImage: ( NSImage * ) image toIconWidth: ( int ) iconWidth usingImageInterpolation: ( NSImageInterpolation ) imageInterpolation NSGraphicsContext * graphicsContext ; NSImageInterpolation previousImageInterpolation ; // NSBitmapImageRep* newBitmapImageRep; // unsigned char* bitmapData; // NSImageRep* originalImageRep; NSImageRep * workingImageRep ; NSSize size , pixelSize , newSize ; // 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 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 ]; if ( size . width >= size . height ) { newSize . width = iconWidth ; newSize . height = floor ( ( float ) iconWidth * size . height / size . width + 0.5 ); 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 )]; iconRect . origin . x = iconRect . origin . y = 0 ; iconRect . size . width = iconRect . size . height = iconWidth ; [[ NSColor clearColor ] set ]; // Set current graphics context to use antialiasing and high-quality 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 ]; #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 bitsPerSample :[ originalImageRep bitsPerSample ] samplesPerPixel :[( NSBitmapImageRep * ) originalImageRep samplesPerPixel ] hasAlpha :[ originalImageRep hasAlpha ] colorSpaceName :[ originalImageRep colorSpaceName ] [ newImage addRepresentation : newBitmapImageRep ]; [ newImage setScalesWhenResized : YES ]; [ newBitmapImageRep release ]; // bitmapData = [newBitmapImageRep bitmapData]; // memset( bitmapData, 128, iconWidth * iconWidth * 4 ); // Copy the original image into the new bitmap, rescaling it to fit. [ 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 ]; return [ newImage autorelease ]; + ( Handle ) get32BitDataFromBitmapImageRep: ( NSBitmapImageRep * ) bitmapImageRep requiredPixelSize: ( int ) requiredPixelSize // 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 ) // 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 NSLog ( @"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES" ); NSLog ( @"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d" , bitsPerSample ); if ((( samplesPerPixel == 3 ) && ( bitsPerPixel == 24 )) || (( samplesPerPixel == 4 ) && ( bitsPerPixel == 32 ))) rawDataSize = pixelsWide * pixelsHigh * 4 ; hRawData = NewHandle ( rawDataSize ); 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 ); oneOverAlpha = 255.0f / ( float ) alphaByte ; * pDest ++ = * ( pSrc + 0 ) * oneOverAlpha ; * pDest ++ = * ( pSrc + 1 ) * oneOverAlpha ; * pDest ++ = * ( pSrc + 2 ) * oneOverAlpha ; } else if ( bitsPerPixel == 24 ) { for ( y = 0 ; y < pixelsHigh ; y ++ ) { pSrc = bitmapData + y * bytesPerRow ; for ( x = 0 ; x < pixelsWide ; x ++ ) { NSLog ( @"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %" , samplesPerPixel , bitsPerPixel ); + ( Handle ) get8BitDataFromBitmapImageRep: ( NSBitmapImageRep * ) bitmapImageRep requiredPixelSize: ( int ) requiredPixelSize // 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 ) // 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... NSLog ( @"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES" ); NSLog ( @"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d" , bitsPerSample ); if ((( samplesPerPixel == 3 ) && ( bitsPerPixel == 24 )) || (( samplesPerPixel == 4 ) && ( bitsPerPixel == 32 ))) CGDirectPaletteRef cgPal ; rawDataSize = pixelsWide * pixelsHigh ; hRawData = NewHandle ( rawDataSize ); cgPal = CGPaletteCreateDefaultColorPalette (); 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 ); } 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 ); NSLog ( @"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %" , samplesPerPixel , bitsPerPixel ); + ( Handle ) get8BitMaskFromBitmapImageRep: ( NSBitmapImageRep * ) bitmapImageRep requiredPixelSize: ( int ) requiredPixelSize // 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 ) // 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... NSLog ( @"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES" ); NSLog ( @"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d" , bitsPerSample ); if ((( samplesPerPixel == 1 ) && ( bitsPerPixel == 8 )) || (( samplesPerPixel == 3 ) && ( bitsPerPixel == 24 )) || (( samplesPerPixel == 4 ) && ( bitsPerPixel == 32 ))) rawDataSize = pixelsWide * pixelsHigh ; hRawData = NewHandle ( rawDataSize ); if ( bitsPerPixel == 32 ) { for ( y = 0 ; y < pixelsHigh ; y ++ ) { pSrc = bitmapData + y * bytesPerRow ; for ( x = 0 ; x < pixelsWide ; x ++ ) { else if ( bitsPerPixel == 24 ) { memset ( pDest , 255 , rawDataSize ); else if ( bitsPerPixel == 8 ) { for ( y = 0 ; y < pixelsHigh ; y ++ ) { memcpy ( pDest , pSrc , pixelsWide ); NSLog ( @"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %" , samplesPerPixel , bitsPerPixel ); // NOTE: This method hasn't been fully tested yet. + ( Handle ) get1BitMaskFromBitmapImageRep: ( NSBitmapImageRep * ) bitmapImageRep requiredPixelSize: ( int ) requiredPixelSize // 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 ) // 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... NSLog ( @"get1BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES" ); 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 ( bitsPerPixel == 32 ) { for ( y = 0 ; y < pixelsHigh ; y ++ ) { pSrc = bitmapData + y * bytesPerRow ; for ( x = 0 ; x < pixelsWide ; x += 8 ) { 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 ; 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 |= * 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 ; else if ( bitsPerPixel == 1 ) { for ( y = 0 ; y < pixelsHigh ; y ++ ) { memcpy ( pDest , pSrc , pixelsWide / 8 ); memcpy ( pRawData + ( pixelsWide * pixelsHigh ) / 8 , pRawData , ( pixelsWide * pixelsHigh ) / 8 ); NSLog ( @"get1BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerPixel == %d, samplesPerPixel== %d, bitsPerSample == %d" , bitsPerPixel , samplesPerPixel , bitsPerSample ); - ( BOOL ) addResourceType: ( OSType ) type asResID: ( int ) resID Handle hIconRes = NewHandle ( 0 ); err = GetIconFamilyData ( hIconFamily , type , hIconRes ); if ( ! GetHandleSize ( hIconRes ) || err != noErr ) AddResource ( hIconRes , type , resID , "\p" ); // Methods for interfacing with the Carbon Scrap Manager (analogous to and // interoperable with the Cocoa Pasteboard). @implementation IconFamily (ScrapAdditions) + ( BOOL ) canInitWithScrap ScrapFlavorInfo * scrapInfos = NULL ; GetScrapFlavorCount ( scrap , & numInfos ); scrapInfos = malloc ( sizeof ( ScrapFlavorInfo ) * numInfos ); GetScrapFlavorInfoList ( scrap , & numInfos , scrapInfos ); for ( i = 0 ; i < numInfos ; i ++ ) if ( scrapInfos [ i ]. flavorType == ' icns ' ) + ( IconFamily * ) iconFamilyWithScrap return [[[ IconFamily alloc ] initWithScrap ] autorelease ]; Handle storageMem = NULL ; if (( self = [ super init ])) GetScrapFlavorSize ( scrap , ' icns ' , & amountMem ); storageMem = NewHandle ( amountMem ); GetScrapFlavorData ( scrap , ' icns ' , & amountMem , * storageMem ); hIconFamily = ( IconFamilyHandle ) storageMem ; HLock (( Handle ) hIconFamily ); PutScrapFlavor ( scrap , ' icns ' , kScrapFlavorMaskNone , GetHandleSize (( Handle ) hIconFamily ), * hIconFamily ); HUnlock (( Handle ) hIconFamily );