diff options
Diffstat (limited to 'Plugins/BuiltInFunctions/IconFamily')
4 files changed, 1696 insertions, 0 deletions
diff --git a/Plugins/BuiltInFunctions/IconFamily/IconFamily/IconFamily.h b/Plugins/BuiltInFunctions/IconFamily/IconFamily/IconFamily.h new file mode 100644 index 0000000..443ee13 --- /dev/null +++ b/Plugins/BuiltInFunctions/IconFamily/IconFamily/IconFamily.h @@ -0,0 +1,184 @@ +// IconFamily.h +// IconFamily class interface +// 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.4. :-) + +#import <Cocoa/Cocoa.h> +#import <Carbon/Carbon.h> + +// This class is a Cocoa/Objective-C wrapper for the Mac OS X Carbon API's +// "icon family" data type. Its main purpose is to enable Cocoa applications +// to easily create custom file icons from NSImage instances, and thus take +// advantage of Mac OS X's new 128x128 RGBA "thumbnail" icon format to provide +// richly detailed thumbnail previews of the files' contents. +// +// Using IconFamily, this becomes as simple as: +// +// id iconFamily = [IconFamily iconFamilyWithThumbnailsOfImage:anImage]; +// [iconFamily setAsCustomIconForFile:anExistingFile]; +// +// You can also write an icon family to an .icns file using the -writeToFile: +// method. + +@interface IconFamily : NSObject +{ + IconFamilyHandle hIconFamily; +} + +// Convenience methods. These use the corresponding -init methods to return +// an autoreleased IconFamily instance. +// +// NOTE: +iconFamily relies on -init, which is currently broken (see -init). + ++ (IconFamily*) iconFamily; ++ (IconFamily*) iconFamilyWithContentsOfFile:(NSString*)path; ++ (IconFamily*) iconFamilyWithIconOfFile:(NSString*)path; ++ (IconFamily*) iconFamilyWithIconFamilyHandle:(IconFamilyHandle)hNewIconFamily; ++ (IconFamily*) iconFamilyWithSystemIcon:(int)fourByteCode; ++ (IconFamily*) iconFamilyWithThumbnailsOfImage:(NSImage*)image; ++ (IconFamily*) iconFamilyWithThumbnailsOfImage:(NSImage*)image usingImageInterpolation:(NSImageInterpolation)imageInterpolation; + +// Initializes as a new, empty IconFamily. This is IconFamily's designated +// initializer method. +// +// NOTE: This method is broken until we figure out how to create a valid new +// IconFamilyHandle! In the meantime, use -initWithContentsOfFile: to +// load an existing .icns file that you can use as a starting point, and +// use -setIconFamilyElement:fromBitmapImageRep: to replace its +// elements. This is what the "MakeThumbnail" demo app does. + +- init; + +// Initializes an IconFamily by loading the contents of an .icns file. + +- initWithContentsOfFile:(NSString*)path; + +// Initializes an IconFamily from an existing Carbon IconFamilyHandle. + +- initWithIconFamilyHandle:(IconFamilyHandle)hNewIconFamily; + +// Initializes an IconFamily by loading the Finder icon that's assigned to a +// file. + +- initWithIconOfFile:(NSString*)path; + +// Initializes an IconFamily by referencing a standard system icon. + +- initWithSystemIcon:(int)fourByteCode; + +// Initializes an IconFamily by creating its elements from a resampled +// NSImage. The second form of this method allows you to specify the degree +// of antialiasing to be used in resampling the image, by passing in one of +// the NSImageInterpolation... constants that are defined in +// NSGraphicsContext.h. The first form of this initializer simply calls the +// second form with imageInterpolation set to NSImageInterpolationHigh, which +// produces highly smoothed thumbnails. + +- initWithThumbnailsOfImage:(NSImage*)image; +- initWithThumbnailsOfImage:(NSImage*)image usingImageInterpolation:(NSImageInterpolation)imageInterpolation; + +// Writes the icon family to an .icns file. + +- (BOOL) writeToFile:(NSString*)path; + +// Sets the image data for one of the icon family's elements from an +// NSBitmapImageRep. The "elementType" parameter must be one of the icon +// family element types listed below, and the format of the "bitmapImageRep" +// must match the corresponding requirements specified below. Regardless of +// the elementType, the bitmapImageRep must also be non-planar and have 8 bits +// per sample. +// +// elementType dimensions format +// ------------------- ---------- --------------------------------------- +// kThumbnail32BitData 128 x 128 32-bit RGBA, 32-bit RGB, or 24-bit RGB +// kThumbnail8BitMask 128 x 128 32-bit RGBA or 8-bit intensity +// kLarge32BitData 32 x 32 32-bit RGBA, 32-bit RGB, or 24-bit RGB +// kLarge8BitMask 32 x 32 32-bit RGBA or 8-bit intensity +// kLarge1BitMask 32 x 32 32-bit RGBA, 8-bit intensity, or 1-bit +// kSmall32BitData 16 x 16 32-bit RGBA, 32-bit RGB, or 24-bit RGB +// kSmall8BitMask 16 x 16 32-bit RGBA or 8-bit intensity +// kSmall1BitMask 16 x 16 32-bit RGBA, 8-bit intensity, or 1-bit +// +// When an RGBA image is supplied to set a "Mask" element, the mask data is +// taken from the image's alpha channel. +// +// NOTE: Setting an IconFamily's kLarge1BitMask seems to damage the IconFamily +// for some as yet unknown reason. (If you then assign the icon family +// as a file's custom icon using -setAsCustomIconForFile:, the custom +// icon doesn't appear for the file in the Finder.) However, both +// custom icon display and mouse-click hit-testing in the Finder seem to +// work fine when we only set the other four elements (thus keeping the +// existing kLarge1BitMask from the valid icon family from which we +// initialized the IconFamily via -initWithContentsOfFile:, since +// IconFamily's -init method is currently broken...), so it seems safe +// to just leave the kLarge1BitMask alone. + +- (BOOL) setIconFamilyElement:(OSType)elementType + fromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep; + +// Gets the image data for one of the icon family's elements as a new, 32-bit +// RGBA NSBitmapImageRep. The specified elementType should be one of +// kThumbnail32BitData, kLarge32BitData, or kSmall32BitData. +// +// The returned NSBitmapImageRep will have the corresponding 8-bit mask data +// in its alpha channel, or a fully opaque alpha channel if the icon family +// has no 8-bit mask data for the specified alpha channel. +// +// Returns nil if the requested element cannot be retrieved (e.g. if the +// icon family has no such 32BitData element). + +- (NSBitmapImageRep*) bitmapImageRepWithAlphaForIconFamilyElement:(OSType)elementType; + +// Creates and returns an NSImage that contains the icon family's various +// elements as its NSImageReps. + +- (NSImage*) imageWithAllReps; + +// NOTE: Planned method -- not yet implemented. +// +// Gets the image data for one of the icon family's elements as a new +// NSBitmapImageRep. The specified elementType should be one of +// kThumbnail32BitData, kThumbnail32BitMask, kLarge32BitData, kLarge8BitMask, +// kLarge1BitMask, kSmall32BitData, kSmall8BitMask, or kSmall1BitMask. + +// - (NSBitmapImageRep*) bitmapImageRepForIconFamilyElement:(OSType)elementType; + +// Writes the icon family to the resource fork of the specified file as its +// kCustomIconResource, and sets the necessary Finder bits so the icon will +// be displayed for the file in Finder views. + +- (BOOL) setAsCustomIconForFile:(NSString*)path; +- (BOOL) setAsCustomIconForFile:(NSString*)path withCompatibility:(BOOL)compat; + +// Same as the -setAsCustomIconForFile:... methods, but for folders (directories). + +- (BOOL) setAsCustomIconForDirectory:(NSString*)path; +- (BOOL) setAsCustomIconForDirectory:(NSString*)path withCompatibility:(BOOL)compat; + +// Removes the custom icon (if any) from the specified file's resource fork, +// and clears the necessary Finder bits for the file. (Note that this is a +// class method, so you don't need an instance of IconFamily to invoke it.) + ++ (BOOL) removeCustomIconFromFile:(NSString*)path; + +@property IconFamilyHandle hIconFamily; +@end + +// Methods for interfacing with the Carbon Scrap Manager (analogous to and +// interoperable with the Cocoa Pasteboard). +@interface IconFamily (ScrapAdditions) ++ (BOOL) canInitWithScrap; ++ (IconFamily*) iconFamilyWithScrap; +- initWithScrap; +- (BOOL) putOnScrap; +@end diff --git a/Plugins/BuiltInFunctions/IconFamily/IconFamily/IconFamily.m b/Plugins/BuiltInFunctions/IconFamily/IconFamily/IconFamily.m new file mode 100644 index 0000000..2ce64f4 --- /dev/null +++ b/Plugins/BuiltInFunctions/IconFamily/IconFamily/IconFamily.m @@ -0,0 +1,1426 @@ +// 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" + +#define unknownOSType 0x3f3f3f3f + +@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; +} + +- (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++; + pRawBitmapData++; + *pRawBitmapData = (*pRawBitmapData << 8) | *pRawMaskData++; + } + HUnlock( hRawMaskData ); + } else { + while (pRawBitmapData < pRawBitmapDataEnd) { + //*pRawBitmapData++ = (*pRawBitmapData << 8) | 0xff; + pRawBitmapData++; + *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 = unknownOSType, existingCreator = unknownOSType; + + // 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, unknownOSType, unknownOSType, 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; +} + +@synthesize hIconFamily; +@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; + unsigned 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 + + diff --git a/Plugins/BuiltInFunctions/IconFamily/IconFamily/NSString+CarbonFSSpecCreation.h b/Plugins/BuiltInFunctions/IconFamily/IconFamily/NSString+CarbonFSSpecCreation.h new file mode 100644 index 0000000..13cd3b9 --- /dev/null +++ b/Plugins/BuiltInFunctions/IconFamily/IconFamily/NSString+CarbonFSSpecCreation.h @@ -0,0 +1,20 @@ +#import <Foundation/Foundation.h> +#import <Carbon/Carbon.h> + +@interface NSString (CarbonFSSpecCreation) + +// Fills in the given FSRef struct so it specifies the file whose path is in this string. +// If the file doesn't exist, and "createFile" is YES, this method will attempt to create +// an empty file with the specified path. (The caller should insure that the directory +// the file is to be placed in already exists.) + +- (BOOL) getFSRef:(FSRef*)fsRef createFileIfNecessary:(BOOL)createFile; + +// Fills in the given FSSpec struct so it specifies the file whose path is in this string. +// If the file doesn't exist, and "createFile" is YES, this method will attempt to create +// an empty file with the specified path. (The caller should insure that the directory +// the file is to be placed in already exists.) + +- (BOOL) getFSSpec:(FSSpec*)fsSpec createFileIfNecessary:(BOOL)createFile; + +@end diff --git a/Plugins/BuiltInFunctions/IconFamily/IconFamily/NSString+CarbonFSSpecCreation.m b/Plugins/BuiltInFunctions/IconFamily/IconFamily/NSString+CarbonFSSpecCreation.m new file mode 100644 index 0000000..711df94 --- /dev/null +++ b/Plugins/BuiltInFunctions/IconFamily/IconFamily/NSString+CarbonFSSpecCreation.m @@ -0,0 +1,66 @@ +#import "NSString+CarbonFSSpecCreation.h" + +@implementation NSString (CarbonFSSpecCreation) + +- (BOOL) getFSRef:(FSRef*)fsRef createFileIfNecessary:(BOOL)createFile +{ + NSFileManager* fileManager = [NSFileManager defaultManager]; + CFURLRef urlRef; + Boolean gotFSRef; + + // Check whether the file exists already. If not, create an empty file if requested. + if (![fileManager fileExistsAtPath:self]) { + if (createFile) { + if (![@"" writeToFile:self atomically:YES]) { + return NO; + } + } else { + return NO; + } + } + + // Create a CFURL with the specified POSIX path. + urlRef = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, + (CFStringRef) self, + kCFURLPOSIXPathStyle, + FALSE /* isDirectory */ ); + if (urlRef == NULL) { +// printf( "** Couldn't make a CFURLRef for the file.\n" ); + return NO; + } + + // Try to create an FSRef from the URL. (If the specified file doesn't exist, this + // function will return false, but if we've reached this code we've already insured + // that the file exists.) + gotFSRef = CFURLGetFSRef( urlRef, fsRef ); + CFRelease( urlRef ); + + if (!gotFSRef) { +// printf( "** Couldn't get an FSRef for the file.\n" ); + return NO; + } + + return YES; +} + +- (BOOL) getFSSpec:(FSSpec*)fsSpec createFileIfNecessary:(BOOL)createFile +{ + FSRef fsRef; + + if (![self getFSRef:&fsRef createFileIfNecessary:createFile]) + return NO; + + if (FSGetCatalogInfo( &fsRef, + kFSCatInfoNone, + NULL, + NULL, + fsSpec, + NULL ) != noErr) { + // printf( "** Couldn't get an FSSpec for the file.\n" ); + return NO; + } + + return YES; +} + +@end |