OLD | NEW |
(Empty) | |
| 1 // IconFamily.m |
| 2 // IconFamily class implementation |
| 3 // by Troy Stephens, Thomas Schnitzer, David Remahl, Nathan Day, Ben Haller, Sve
n Janssen, Peter Hosey, Conor Dearden, Elliot Glaysher, and Dave MacLachlan |
| 4 // version 0.9.4 |
| 5 // |
| 6 // Project Home Page: |
| 7 // http://iconfamily.sourceforge.net/ |
| 8 // |
| 9 // Problems, shortcomings, and uncertainties that I'm aware of are flagged with
"NOTE:". Please address bug reports, bug fixes, suggestions, etc. to the projec
t Forums and bug tracker at https://sourceforge.net/projects/iconfamily/ |
| 10 |
| 11 /* |
| 12 Copyright (c) 2001-2010 Troy N. Stephens |
| 13 Portions Copyright (c) 2007 Google Inc. |
| 14 |
| 15 Use and distribution of this source code is governed by the MIT License, who
se terms are as follows. |
| 16 |
| 17 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal i
n the Software without restriction, including without limitation the rights to u
se, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: |
| 18 |
| 19 The above copyright notice and this permission notice shall be included in a
ll copies or substantial portions of the Software. |
| 20 |
| 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
MPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR C
OPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 22 */ |
| 23 |
| 24 #import "IconFamily.h" |
| 25 #import "NSString+CarbonFSRefCreation.h" |
| 26 |
| 27 |
| 28 @interface NSFileManager (IconFamilyCompatibility) |
| 29 |
| 30 - (NSDictionary *) iconfamily_attributesAtPath:(NSString *)path; |
| 31 - (BOOL) iconfamily_setAttributes:(NSDictionary *)attributes atPath:(NSString *)
path; |
| 32 - (BOOL) iconfamily_removeItemAtPath:(NSString *)path; |
| 33 |
| 34 @end |
| 35 |
| 36 |
| 37 @interface NSImage (IconFamilyCompatibility) |
| 38 |
| 39 - (NSImageRep *) iconfamily_bestRepresentation; |
| 40 |
| 41 @end |
| 42 |
| 43 |
| 44 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050 |
| 45 // Methods defined in 10.6 and beyond |
| 46 @interface NSImage (SnowLeopard) |
| 47 |
| 48 - (NSImageRep *)bestRepresentationForRect:(NSRect)rect context:(NSGraphicsContex
t *)referenceContext hints:(NSDictionary *)hints; |
| 49 |
| 50 @end |
| 51 #endif |
| 52 |
| 53 |
| 54 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 |
| 55 // This is defined in 10.5 and beyond in IconStorage.h |
| 56 enum { |
| 57 kIconServices512PixelDataARGB = 'ic09' /* non-premultiplied 512x512 ARGB bitma
p*/ |
| 58 }; |
| 59 |
| 60 // Methods defined in 10.5 and beyond |
| 61 @interface NSFileManager (Leopard) |
| 62 |
| 63 - (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)erro
r; |
| 64 - (BOOL)setAttributes:(NSDictionary *)attributes ofItemAtPath:(NSString *)path e
rror:(NSError **)error; |
| 65 - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error; |
| 66 |
| 67 @end |
| 68 #endif |
| 69 |
| 70 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3 |
| 71 // This is defined in 10.4 and beyond in IconStorage.h |
| 72 enum { |
| 73 kIconServices256PixelDataARGB = 'ic08' /* non-premultiplied 256x256 ARGB bitma
p*/ |
| 74 }; |
| 75 #endif |
| 76 |
| 77 // Necessary on 10.5 for Preview's "New with Clipboard" menu item to see the Ico
nFamily data. |
| 78 #define ICONFAMILY_UTI @"com.apple.icns" |
| 79 // Determined by using Pasteboard Manager to put com.apple.icns data on the clip
board. Alternatively, you can determine this by copying an application to the cl
ipboard using the Finder (select an application and press cmd-C). |
| 80 #define ICONFAMILY_PBOARD_TYPE @"'icns' (CorePasteboardFlavorType 0x69636E73)" |
| 81 |
| 82 @interface IconFamily (Internals) |
| 83 |
| 84 + (NSImage*) resampleImage:(NSImage*)image toIconWidth:(int)width usingImageInte
rpolation:(NSImageInterpolation)imageInterpolation; |
| 85 |
| 86 + (Handle) get32BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requ
iredPixelSize:(int)requiredPixelSize; |
| 87 |
| 88 + (Handle) get8BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requi
redPixelSize:(int)requiredPixelSize; |
| 89 |
| 90 + (Handle) get8BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requi
redPixelSize:(int)requiredPixelSize; |
| 91 |
| 92 + (Handle) get1BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requi
redPixelSize:(int)requiredPixelSize; |
| 93 |
| 94 - (BOOL) addResourceType:(OSType)type asResID:(int)resID; |
| 95 |
| 96 @end |
| 97 |
| 98 @implementation IconFamily |
| 99 |
| 100 + (IconFamily*) iconFamily |
| 101 { |
| 102 return [[[IconFamily alloc] init] autorelease]; |
| 103 } |
| 104 |
| 105 + (IconFamily*) iconFamilyWithContentsOfFile:(NSString*)path |
| 106 { |
| 107 return [[[IconFamily alloc] initWithContentsOfFile:path] autorelease]; |
| 108 } |
| 109 |
| 110 + (IconFamily*) iconFamilyWithIconOfFile:(NSString*)path |
| 111 { |
| 112 return [[[IconFamily alloc] initWithIconOfFile:path] autorelease]; |
| 113 } |
| 114 |
| 115 + (IconFamily*) iconFamilyWithIconFamilyHandle:(IconFamilyHandle)hNewIconFamily |
| 116 { |
| 117 return [[[IconFamily alloc] initWithIconFamilyHandle:hNewIconFamily] autorel
ease]; |
| 118 } |
| 119 |
| 120 + (IconFamily*) iconFamilyWithSystemIcon:(int)fourByteCode |
| 121 { |
| 122 return [[[IconFamily alloc] initWithSystemIcon:fourByteCode] autorelease]; |
| 123 } |
| 124 |
| 125 + (IconFamily*) iconFamilyWithThumbnailsOfImage:(NSImage*)image |
| 126 { |
| 127 return [[[IconFamily alloc] initWithThumbnailsOfImage:image] autorelease]; |
| 128 } |
| 129 |
| 130 + (IconFamily*) iconFamilyWithThumbnailsOfImage:(NSImage*)image usingImageInterp
olation:(NSImageInterpolation)imageInterpolation |
| 131 { |
| 132 return [[[IconFamily alloc] initWithThumbnailsOfImage:image usingImageInterp
olation:imageInterpolation] autorelease]; |
| 133 } |
| 134 |
| 135 // This is IconFamily's designated initializer. It creates a new IconFamily tha
t initially has no elements. |
| 136 // |
| 137 // The proper way to do this is to simply allocate a zero-sized handle (not to b
e confused with an empty handle) and assign it to hIconFamily. This technique w
orks on Mac OS X 10.2 as well as on 10.0.x and 10.1.x. Our previous technique o
f allocating an IconFamily struct with a resourceSize of 0 no longer works as of
Mac OS X 10.2. |
| 138 - init |
| 139 { |
| 140 self = [super init]; |
| 141 if (self) { |
| 142 hIconFamily = (IconFamilyHandle) NewHandle( 0 ); |
| 143 if (hIconFamily == NULL) { |
| 144 [self autorelease]; |
| 145 return nil; |
| 146 } |
| 147 } |
| 148 return self; |
| 149 } |
| 150 |
| 151 - initWithData:(NSData *)data |
| 152 { |
| 153 self = [self init]; |
| 154 if (self) { |
| 155 Handle storageMem = NULL; |
| 156 |
| 157 OSStatus err = PtrToHand([data bytes], &storageMem, (long)[data length])
; |
| 158 if( err != noErr ) |
| 159 { |
| 160 [self release]; |
| 161 return nil; |
| 162 } |
| 163 |
| 164 hIconFamily = (IconFamilyHandle)storageMem; |
| 165 } |
| 166 return self; |
| 167 } |
| 168 |
| 169 - initWithContentsOfFile:(NSString*)path |
| 170 { |
| 171 FSRef ref; |
| 172 OSStatus result; |
| 173 |
| 174 self = [self init]; |
| 175 if (self) { |
| 176 if (hIconFamily) { |
| 177 DisposeHandle( (Handle)hIconFamily ); |
| 178 hIconFamily = NULL; |
| 179 } |
| 180 if (![path getFSRef:&ref createFileIfNecessary:NO]) { |
| 181 [self autorelease]; |
| 182 return nil; |
| 183 } |
| 184 result = ReadIconFromFSRef( &ref, &hIconFamily ); |
| 185 if (result != noErr) { |
| 186 [self autorelease]; |
| 187 return nil; |
| 188 } |
| 189 } |
| 190 return self; |
| 191 } |
| 192 |
| 193 - initWithIconFamilyHandle:(IconFamilyHandle)hNewIconFamily |
| 194 { |
| 195 self = [self init]; |
| 196 if (self) { |
| 197 if (hIconFamily) { |
| 198 DisposeHandle( (Handle)hIconFamily ); |
| 199 hIconFamily = NULL; |
| 200 } |
| 201 hIconFamily = hNewIconFamily; |
| 202 } |
| 203 return self; |
| 204 } |
| 205 |
| 206 - initWithIconOfFile:(NSString*)path |
| 207 { |
| 208 IconRef iconRef; |
| 209 OSStatus result; |
| 210 SInt16 label; |
| 211 FSRef ref; |
| 212 |
| 213 self = [self init]; |
| 214 if (self) |
| 215 { |
| 216 if (hIconFamily) |
| 217 { |
| 218 DisposeHandle( (Handle)hIconFamily ); |
| 219 hIconFamily = NULL; |
| 220 } |
| 221 |
| 222 if( ![path getFSRef:&ref createFileIfNecessary:NO] ) |
| 223 { |
| 224 [self autorelease]; |
| 225 return nil; |
| 226 } |
| 227 |
| 228 result = GetIconRefFromFileInfo( |
| 229 &ref, |
| 230 /*inFileNameLength*/ 0, |
| 231 /*inFileName*/ NULL, |
| 232 kFSCatInfoNone, |
| 233 /*inCatalogInfo*/ NULL, |
| 234 kIconServicesNormalUsageFlag, |
| 235 &iconRef, |
| 236 &label ); |
| 237 |
| 238 if (result != noErr) |
| 239 { |
| 240 [self autorelease]; |
| 241 return nil; |
| 242 } |
| 243 |
| 244 result = IconRefToIconFamily( |
| 245 iconRef, |
| 246 kSelectorAllAvailableData, |
| 247 &hIconFamily ); |
| 248 |
| 249 ReleaseIconRef( iconRef ); |
| 250 |
| 251 if (result != noErr || !hIconFamily) |
| 252 { |
| 253 [self autorelease]; |
| 254 return nil; |
| 255 } |
| 256 } |
| 257 return self; |
| 258 } |
| 259 |
| 260 - initWithSystemIcon:(int)fourByteCode |
| 261 { |
| 262 IconRef iconRef; |
| 263 OSErr result; |
| 264 |
| 265 self = [self init]; |
| 266 if (self) |
| 267 { |
| 268 if (hIconFamily) |
| 269 { |
| 270 DisposeHandle( (Handle)hIconFamily ); |
| 271 hIconFamily = NULL; |
| 272 } |
| 273 |
| 274 result = GetIconRef(kOnSystemDisk, kSystemIconsCreator, fourByteCode, &i
conRef); |
| 275 |
| 276 if (result != noErr) |
| 277 { |
| 278 [self autorelease]; |
| 279 return nil; |
| 280 } |
| 281 |
| 282 result = IconRefToIconFamily( |
| 283 iconRef, |
| 284 kSelectorAllAvailableData, |
| 285 &hIconFamily ); |
| 286 |
| 287 if (result != noErr || !hIconFamily) |
| 288 { |
| 289 [self autorelease]; |
| 290 return nil; |
| 291 } |
| 292 |
| 293 ReleaseIconRef( iconRef ); |
| 294 } |
| 295 return self; |
| 296 } |
| 297 |
| 298 - initWithThumbnailsOfImage:(NSImage*)image |
| 299 { |
| 300 // The default is to use a high degree of antialiasing, producing a smooth i
mage. |
| 301 return [self initWithThumbnailsOfImage:image usingImageInterpolation:NSImage
InterpolationHigh]; |
| 302 } |
| 303 |
| 304 - initWithThumbnailsOfImage:(NSImage*)image usingImageInterpolation:(NSImageInte
rpolation)imageInterpolation |
| 305 { |
| 306 NSImage* iconImage512x512; |
| 307 NSImage* iconImage256x256; |
| 308 NSImage* iconImage128x128; |
| 309 NSImage* iconImage32x32; |
| 310 NSImage* iconImage16x16; |
| 311 NSImage* bitmappedIconImage512x512; |
| 312 NSBitmapImageRep* iconBitmap512x512; |
| 313 NSBitmapImageRep* iconBitmap256x256; |
| 314 NSBitmapImageRep* iconBitmap128x128; |
| 315 NSBitmapImageRep* iconBitmap32x32; |
| 316 NSBitmapImageRep* iconBitmap16x16; |
| 317 |
| 318 // Start with a new, empty IconFamily. |
| 319 self = [self init]; |
| 320 if (self == nil) |
| 321 return nil; |
| 322 |
| 323 // Resample the given image to create a 512x512 pixel, 32-bit RGBA |
| 324 // version, and use that as our "thumbnail" (512x512) icon and mask. |
| 325 // |
| 326 // Our +resampleImage:toIconWidth:... method, in its present form, |
| 327 // returns an NSImage that contains an NSCacheImageRep, rather than |
| 328 // an NSBitmapImageRep. We convert to an NSBitmapImageRep, so that |
| 329 // our methods can scan the image data, using initWithFocusedViewRect:. |
| 330 iconImage512x512 = [IconFamily resampleImage:image toIconWidth:512 usingImag
eInterpolation:imageInterpolation]; |
| 331 if (!iconImage512x512) { |
| 332 [self autorelease]; |
| 333 return nil; |
| 334 } |
| 335 |
| 336 [iconImage512x512 lockFocus]; |
| 337 iconBitmap512x512 = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMak
eRect(0, 0, 512, 512)] autorelease]; |
| 338 [iconImage512x512 unlockFocus]; |
| 339 if (!iconBitmap512x512) { |
| 340 [self release]; |
| 341 return nil; |
| 342 } |
| 343 // Create an NSImage with the iconBitmap512x512 NSBitmapImageRep, that we |
| 344 // can resample to create the smaller icon family elements. (This is |
| 345 // most likely more efficient than resampling from the original image again, |
| 346 // particularly if it is large. It produces a slightly different result, bu
t |
| 347 // the difference is minor and should not be objectionable...) |
| 348 |
| 349 bitmappedIconImage512x512 = [[NSImage alloc] initWithSize:NSMakeSize(512, 51
2)]; |
| 350 [bitmappedIconImage512x512 addRepresentation:iconBitmap512x512]; |
| 351 |
| 352 if (!bitmappedIconImage512x512) { |
| 353 [self autorelease]; |
| 354 return nil; |
| 355 } |
| 356 |
| 357 [self setIconFamilyElement:kIconServices512PixelDataARGB fromBitmapImageRep:
iconBitmap512x512]; |
| 358 |
| 359 iconImage256x256 = [IconFamily resampleImage:bitmappedIconImage512x512 toIco
nWidth:256 usingImageInterpolation:imageInterpolation]; |
| 360 if (iconImage256x256) { |
| 361 [iconImage256x256 lockFocus]; |
| 362 iconBitmap256x256 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMa
keRect(0, 0, 256, 256)]; |
| 363 [iconImage256x256 unlockFocus]; |
| 364 if (iconBitmap256x256) { |
| 365 [self setIconFamilyElement:kIconServices256PixelDataARGB fromBitmapImage
Rep:iconBitmap256x256]; |
| 366 [iconBitmap256x256 release]; |
| 367 } |
| 368 } |
| 369 |
| 370 iconImage128x128 = [IconFamily resampleImage:bitmappedIconImage512x512 toIco
nWidth:128 usingImageInterpolation:imageInterpolation]; |
| 371 if (iconImage128x128) { |
| 372 [iconImage128x128 lockFocus]; |
| 373 iconBitmap128x128 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMa
keRect(0, 0, 128, 128)]; |
| 374 [iconImage128x128 unlockFocus]; |
| 375 |
| 376 if (iconBitmap128x128) { |
| 377 [self setIconFamilyElement:kThumbnail32BitData fromBitmapImageRep:iconBi
tmap128x128]; |
| 378 [self setIconFamilyElement:kThumbnail8BitMask fromBitmapImageRep:iconBi
tmap128x128]; |
| 379 [iconBitmap128x128 release]; |
| 380 } |
| 381 } |
| 382 |
| 383 // Resample the 512x512 image to create a 32x32 pixel, 32-bit RGBA version, |
| 384 // and use that as our "large" (32x32) icon and 8-bit mask. |
| 385 iconImage32x32 = [IconFamily resampleImage:bitmappedIconImage512x512 toIconW
idth:32 usingImageInterpolation:imageInterpolation]; |
| 386 if (iconImage32x32) { |
| 387 [iconImage32x32 lockFocus]; |
| 388 iconBitmap32x32 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMake
Rect(0, 0, 32, 32)]; |
| 389 [iconImage32x32 unlockFocus]; |
| 390 if (iconBitmap32x32) { |
| 391 [self setIconFamilyElement:kLarge32BitData fromBitmapImageRep:iconBitmap
32x32]; |
| 392 [self setIconFamilyElement:kLarge8BitData fromBitmapImageRep:iconBitmap3
2x32]; |
| 393 [self setIconFamilyElement:kLarge8BitMask fromBitmapImageRep:iconBitmap3
2x32]; |
| 394 [self setIconFamilyElement:kLarge1BitMask fromBitmapImageRep:iconBitmap3
2x32]; |
| 395 [iconBitmap32x32 release]; |
| 396 } |
| 397 } |
| 398 |
| 399 // Resample the 512x512 image to create a 16x16 pixel, 32-bit RGBA version, |
| 400 // and use that as our "small" (16x16) icon and 8-bit mask. |
| 401 iconImage16x16 = [IconFamily resampleImage:bitmappedIconImage512x512 toIconW
idth:16 usingImageInterpolation:imageInterpolation]; |
| 402 if (iconImage16x16) { |
| 403 [iconImage16x16 lockFocus]; |
| 404 iconBitmap16x16 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMake
Rect(0, 0, 16, 16)]; |
| 405 [iconImage16x16 unlockFocus]; |
| 406 if (iconBitmap16x16) { |
| 407 [self setIconFamilyElement:kSmall32BitData fromBitmapImageRep:iconBitmap
16x16]; |
| 408 [self setIconFamilyElement:kSmall8BitData fromBitmapImageRep:iconBitmap1
6x16]; |
| 409 [self setIconFamilyElement:kSmall8BitMask fromBitmapImageRep:iconBitmap1
6x16]; |
| 410 [self setIconFamilyElement:kSmall1BitMask fromBitmapImageRep:iconBitmap1
6x16]; |
| 411 [iconBitmap16x16 release]; |
| 412 } |
| 413 } |
| 414 |
| 415 // Release the icon. |
| 416 [bitmappedIconImage512x512 release]; |
| 417 |
| 418 // Return the new icon family! |
| 419 return self; |
| 420 } |
| 421 |
| 422 - (void) dealloc |
| 423 { |
| 424 DisposeHandle( (Handle)hIconFamily ); |
| 425 [super dealloc]; |
| 426 } |
| 427 |
| 428 - (void) finalize |
| 429 { |
| 430 /* "Starting with Mac OS X v10.3, Memory Manager is thread safe" |
| 431 -- Memory Manager Reference |
| 432 */ |
| 433 DisposeHandle( (Handle)hIconFamily ); |
| 434 hIconFamily = NULL; |
| 435 |
| 436 [super finalize]; |
| 437 } |
| 438 |
| 439 - (NSBitmapImageRep*) bitmapImageRepWithAlphaForIconFamilyElement:(OSType)elemen
tType |
| 440 { |
| 441 NSBitmapImageRep* bitmapImageRep; |
| 442 int pixelsWide; |
| 443 Handle hRawBitmapData; |
| 444 Handle hRawMaskData = NULL; |
| 445 OSType maskElementType; |
| 446 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 |
| 447 NSBitmapFormat bitmapFormat = NSAlphaFirstBitmapFormat; |
| 448 #endif |
| 449 OSErr result; |
| 450 UInt32* pRawBitmapData; |
| 451 UInt32* pRawBitmapDataEnd; |
| 452 unsigned char* pRawMaskData; |
| 453 unsigned char* pBitmapImageRepBitmapData; |
| 454 |
| 455 // Make sure elementType is a valid type that we know how to handle, and |
| 456 // figure out the dimensions and bit depth of the bitmap for that type. |
| 457 switch (elementType) { |
| 458 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 459 // 'ic09' 512x512 32-bit RGB image |
| 460 case kIconServices512PixelDataARGB: |
| 461 maskElementType = 0; |
| 462 pixelsWide = 512; |
| 463 break; |
| 464 #endif |
| 465 |
| 466 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 |
| 467 // 'ic08' 256x256 32-bit ARGB image |
| 468 case kIconServices256PixelDataARGB: |
| 469 maskElementType = 0; |
| 470 pixelsWide = 256; |
| 471 break; |
| 472 #endif |
| 473 |
| 474 // 'it32' 128x128 32-bit RGB image |
| 475 case kThumbnail32BitData: |
| 476 maskElementType = kThumbnail8BitMask; |
| 477 pixelsWide = 128; |
| 478 break; |
| 479 |
| 480 // 'ih32' 48x48 32-bit RGB image |
| 481 case kHuge32BitData: |
| 482 maskElementType = kHuge8BitMask; |
| 483 pixelsWide = 48; |
| 484 break; |
| 485 |
| 486 // 'il32' 32x32 32-bit RGB image |
| 487 case kLarge32BitData: |
| 488 maskElementType = kLarge8BitMask; |
| 489 pixelsWide = 32; |
| 490 break; |
| 491 |
| 492 // 'is32' 16x16 32-bit RGB image |
| 493 case kSmall32BitData: |
| 494 maskElementType = kSmall8BitMask; |
| 495 pixelsWide = 16; |
| 496 break; |
| 497 |
| 498 default: |
| 499 return nil; |
| 500 } |
| 501 |
| 502 // Get the raw, uncompressed bitmap data for the requested element. |
| 503 hRawBitmapData = NewHandle( pixelsWide * pixelsWide * 4 ); |
| 504 result = GetIconFamilyData( hIconFamily, elementType, hRawBitmapData ); |
| 505 if (result != noErr) { |
| 506 DisposeHandle( hRawBitmapData ); |
| 507 return nil; |
| 508 } |
| 509 |
| 510 if (maskElementType) { |
| 511 // Get the corresponding raw, uncompressed 8-bit mask data. |
| 512 hRawMaskData = NewHandle( pixelsWide * pixelsWide ); |
| 513 result = GetIconFamilyData( hIconFamily, maskElementType, hRawMaskData )
; |
| 514 if (result != noErr) { |
| 515 DisposeHandle( hRawMaskData ); |
| 516 hRawMaskData = NULL; |
| 517 } |
| 518 } |
| 519 |
| 520 // The retrieved raw bitmap data is stored in memory as 32 bit per pixel, 8
bit per sample xRGB data. (The sample order provided by IconServices is the sam
e, regardless of whether we're running on a big-endian (PPC) or little-endian (I
ntel) architecture.) |
| 521 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 |
| 522 // With proper attention to byte order, we can fold the mask data into the c
olor data in-place, producing ARGB data suitable for handing off to NSBitmapImag
eRep. |
| 523 #else |
| 524 // With proper attention to byte order, we can fold the mask data into the c
olor data in-place, producing RGBA data suitable for handing off to NSBitmapImag
eRep. |
| 525 #endif |
| 526 // HLock( hRawBitmapData ); // Handle-based memory isn't compacted anymore, s
o calling HLock()/HUnlock() is unnecessary. |
| 527 pRawBitmapData = (UInt32*) *hRawBitmapData; |
| 528 pRawBitmapDataEnd = pRawBitmapData + pixelsWide * pixelsWide; |
| 529 if (hRawMaskData) { |
| 530 // HLock( hRawMaskData ); // Handle-based memory isn't compacted anymore,
so calling HLock()/HUnlock() is unnecessary. |
| 531 pRawMaskData = (UInt8*) *hRawMaskData; |
| 532 while (pRawBitmapData < pRawBitmapDataEnd) { |
| 533 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 |
| 534 // Convert the xRGB pixel data to ARGB. |
| 535 // PowerPC
Intel |
| 536 // -------
----- |
| 537 // Bytes in memory are x R G B
x R G B |
| 538 // *pRawBitmapData loads as 32-bit word into register xRGB
BGRx |
| 539 // CFSwapInt32HostToBig() swaps this to xRGB
xRGB |
| 540 // Loading *pRawMaskData and shifting left 24 bits yields A000
A000 |
| 541 // Bitwise ORing these two words together yields ARGB
ARGB |
| 542 // CFSwapInt32BigToHost() swaps this to ARGB
BGRA |
| 543 // Bytes in memory after they're stored as a 32-bit word A R G B
A R G B |
| 544 *pRawBitmapData = CFSwapInt32BigToHost((*pRawMaskData++ << 24) | CFS
wapInt32HostToBig(*pRawBitmapData)); |
| 545 #else |
| 546 // Convert the xRGB pixel data to RGBA. |
| 547 // PowerPC
Intel |
| 548 // -------
----- |
| 549 // Bytes in memory are x R G B
x R G B |
| 550 // *pRawBitmapData loads as 32-bit word into register xRGB
BGRx |
| 551 // CFSwapInt32HostToBig() swaps this to xRGB
xRGB |
| 552 // Shifting left 8 bits yields ('0' denotes all zero bits) RGB0
RGB0 |
| 553 // Bitwise ORing with *pRawMaskData byte yields RGBA
RGBA |
| 554 // CFSwapInt32BigToHost() swaps this to RGBA
ABGR |
| 555 // Bytes in memory after they're stored as a 32-bit word R G B A
R G B A |
| 556 *pRawBitmapData = CFSwapInt32BigToHost((CFSwapInt32HostToBig(*pRawBi
tmapData) << 8) | *pRawMaskData++); |
| 557 #endif |
| 558 ++pRawBitmapData; |
| 559 } |
| 560 // HUnlock( hRawMaskData ); // Handle-based memory isn't compacted anymor
e, so calling HLock()/HUnlock() is unnecessary. |
| 561 } else { |
| 562 if(maskElementType) { |
| 563 // We SHOULD have a mask, but apparently not. Fake it with alpha=1. |
| 564 while (pRawBitmapData < pRawBitmapDataEnd) { |
| 565 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 |
| 566 // Set alpha byte to 0xff. |
| 567 //
PowerPC Intel |
| 568 //
------- ----- |
| 569 // Bytes in memory are
x R G B x R G B |
| 570 // Writing a single 0xff byte ('1') at pRawBitmapData yields
1 R G B 1 R G B |
| 571 *(unsigned char *)pRawBitmapData = 0xff; |
| 572 #else |
| 573 // Set alpha byte to 0xff. |
| 574 //
PowerPC Intel |
| 575 //
------- ----- |
| 576 // Bytes in memory are
R G B x R G B x |
| 577 // Writing a single 0xff byte, 3 bytes past pRawBitmapData yield
s R G B 1 R G B 1 |
| 578 *((unsigned char *)pRawBitmapData + 3) = 0xff; |
| 579 #endif |
| 580 ++pRawBitmapData; |
| 581 } |
| 582 } |
| 583 } |
| 584 |
| 585 // Create a new NSBitmapImageRep with the given bitmap data. Note that |
| 586 // when creating the NSBitmapImageRep we pass in NULL for the "planes" |
| 587 // parameter. This causes the new NSBitmapImageRep to allocate its own |
| 588 // buffer for the bitmap data (which it will own and release when the |
| 589 // NSBitmapImageRep is released), rather than referencing the bitmap |
| 590 // data we pass in (which will soon disappear when we call |
| 591 // DisposeHandle() below!). (See the NSBitmapImageRep documentation for |
| 592 // the -initWithBitmapDataPlanes:... method, where this is explained.) |
| 593 // |
| 594 // Once we have the new NSBitmapImageRep, we get a pointer to its |
| 595 // bitmapData and copy our bitmap data in. |
| 596 bitmapImageRep = [[[NSBitmapImageRep alloc] |
| 597 initWithBitmapDataPlanes:NULL |
| 598 pixelsWide:pixelsWide |
| 599 pixelsHigh:pixelsWide |
| 600 bitsPerSample:8 |
| 601 samplesPerPixel:4 |
| 602 hasAlpha:YES |
| 603 isPlanar:NO |
| 604 colorSpaceName:NSDeviceRGBColorSpace // NOTE: is this right? |
| 605 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 |
| 606 bitmapFormat:bitmapFormat |
| 607 #endif |
| 608 bytesPerRow:0 |
| 609 bitsPerPixel:0] autorelease]; |
| 610 pBitmapImageRepBitmapData = [bitmapImageRep bitmapData]; |
| 611 if (pBitmapImageRepBitmapData) { |
| 612 memcpy( pBitmapImageRepBitmapData, *hRawBitmapData, |
| 613 pixelsWide * pixelsWide * 4 ); |
| 614 } |
| 615 // HUnlock( hRawBitmapData ); // Handle-based memory isn't compacted anymore,
so calling HLock()/HUnlock() is unnecessary. |
| 616 |
| 617 // Free the retrieved raw data. |
| 618 DisposeHandle( hRawBitmapData ); |
| 619 if (hRawMaskData) |
| 620 DisposeHandle( hRawMaskData ); |
| 621 |
| 622 // Return nil if the NSBitmapImageRep didn't give us a buffer to copy into. |
| 623 if (pBitmapImageRepBitmapData == NULL) |
| 624 return nil; |
| 625 |
| 626 // Return the new NSBitmapImageRep. |
| 627 return bitmapImageRep; |
| 628 } |
| 629 |
| 630 - (NSImage*) imageWithAllReps |
| 631 { |
| 632 NSImage* image = NULL; |
| 633 image = [[[NSImage alloc] initWithData:[NSData dataWithBytes:*hIconFamily le
ngth:GetHandleSize((Handle)hIconFamily)]] autorelease]; |
| 634 return image; |
| 635 } |
| 636 |
| 637 - (BOOL) setIconFamilyElement:(OSType)elementType fromBitmapImageRep:(NSBitmapIm
ageRep*)bitmapImageRep |
| 638 { |
| 639 Handle hRawData = NULL; |
| 640 OSErr result; |
| 641 |
| 642 switch (elementType) { |
| 643 // 'ic08' 512x512 32-bit ARGB image |
| 644 case kIconServices512PixelDataARGB: |
| 645 hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep req
uiredPixelSize:512]; |
| 646 break; |
| 647 |
| 648 // 'ic08' 256x256 32-bit ARGB image |
| 649 case kIconServices256PixelDataARGB: |
| 650 hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep req
uiredPixelSize:256]; |
| 651 break; |
| 652 |
| 653 // 'it32' 128x128 32-bit RGB image |
| 654 case kThumbnail32BitData: |
| 655 hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep
requiredPixelSize:128]; |
| 656 break; |
| 657 |
| 658 // 't8mk' 128x128 8-bit alpha mask |
| 659 case kThumbnail8BitMask: |
| 660 hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep
requiredPixelSize:128]; |
| 661 break; |
| 662 |
| 663 // 'il32' 32x32 32-bit RGB image |
| 664 case kLarge32BitData: |
| 665 hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep
requiredPixelSize:32]; |
| 666 break; |
| 667 |
| 668 // 'l8mk' 32x32 8-bit alpha mask |
| 669 case kLarge8BitMask: |
| 670 hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep
requiredPixelSize:32]; |
| 671 break; |
| 672 |
| 673 // 'ICN#' 32x32 1-bit alpha mask |
| 674 case kLarge1BitMask: |
| 675 hRawData = [IconFamily get1BitMaskFromBitmapImageRep:bitmapImageRep
requiredPixelSize:32]; |
| 676 break; |
| 677 |
| 678 // 'icl8' 32x32 8-bit indexed image data |
| 679 case kLarge8BitData: |
| 680 hRawData = [IconFamily get8BitDataFromBitmapImageRep:bitmapImage
Rep requiredPixelSize:32]; |
| 681 break; |
| 682 |
| 683 // 'is32' 16x16 32-bit RGB image |
| 684 case kSmall32BitData: |
| 685 hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImag
eRep requiredPixelSize:16]; |
| 686 break; |
| 687 |
| 688 // 's8mk' 16x16 8-bit alpha mask |
| 689 case kSmall8BitMask: |
| 690 hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep
requiredPixelSize:16]; |
| 691 break; |
| 692 |
| 693 // 'ics#' 16x16 1-bit alpha mask |
| 694 case kSmall1BitMask: |
| 695 hRawData = [IconFamily get1BitMaskFromBitmapImageRep:bitmapImageRep
requiredPixelSize:16]; |
| 696 break; |
| 697 |
| 698 // 'ics8' 16x16 8-bit indexed image data |
| 699 case kSmall8BitData: |
| 700 hRawData = [IconFamily get8BitDataFromBitmapImageRep:bitmapImage
Rep requiredPixelSize:16]; |
| 701 break; |
| 702 |
| 703 default: |
| 704 return NO; |
| 705 } |
| 706 |
| 707 // NSLog(@"setIconFamilyElement:%@ fromBitmapImageRep:%@ generated handl
e %p of size %d", NSFileTypeForHFSTypeCode(elementType), bitmapImageRep, hRawDat
a, GetHandleSize(hRawData)); |
| 708 |
| 709 if (hRawData == NULL) |
| 710 { |
| 711 NSLog(@"Null data returned to setIconFamilyElement:fromBitmapIma
geRep:"); |
| 712 return NO; |
| 713 } |
| 714 |
| 715 result = SetIconFamilyData( hIconFamily, elementType, hRawData ); |
| 716 DisposeHandle( hRawData ); |
| 717 |
| 718 if (result != noErr) |
| 719 { |
| 720 NSLog(@"SetIconFamilyData() returned error %d", result); |
| 721 return NO; |
| 722 } |
| 723 |
| 724 return YES; |
| 725 } |
| 726 |
| 727 - (BOOL) setAsCustomIconForFile:(NSString*)path |
| 728 { |
| 729 return( [self setAsCustomIconForFile:path withCompatibility:NO] ); |
| 730 } |
| 731 |
| 732 - (BOOL) setAsCustomIconForFile:(NSString*)path withCompatibility:(BOOL)compat |
| 733 { |
| 734 FSRef targetFileFSRef; |
| 735 FSRef parentDirectoryFSRef; |
| 736 SInt16 file; |
| 737 OSStatus result; |
| 738 struct FSCatalogInfo catInfo; |
| 739 struct FileInfo *finderInfo = (struct FileInfo *)&catInfo.finderInfo; |
| 740 Handle hExistingCustomIcon; |
| 741 Handle hIconFamilyCopy; |
| 742 NSString *parentDirectory; |
| 743 |
| 744 // Before we do anything, get the original modification time for the target
file. |
| 745 NSDate* modificationDate = [[[NSFileManager defaultManager] iconfamily_attri
butesAtPath:path] objectForKey:NSFileModificationDate]; |
| 746 |
| 747 if ([path isAbsolutePath]) |
| 748 parentDirectory = [path stringByDeletingLastPathComponent]; |
| 749 else |
| 750 parentDirectory = [[[NSFileManager defaultManager] currentDirectoryPath]
stringByAppendingPathComponent:[path stringByDeletingLastPathComponent]]; |
| 751 |
| 752 // Get an FSRef for the target file's parent directory that we can use in |
| 753 // the FSCreateResFile() and FNNotify() calls below. |
| 754 if (![parentDirectory getFSRef:&parentDirectoryFSRef createFileIfNecessary:N
O]) |
| 755 return NO; |
| 756 |
| 757 // Get the name of the file, for FSCreateResFile. |
| 758 struct HFSUniStr255 filename; |
| 759 NSString *filenameString = [path lastPathComponent]; |
| 760 filename.length = [filenameString length]; |
| 761 [filenameString getCharacters:filename.unicode]; |
| 762 |
| 763 // Make sure the file has a resource fork that we can open. (Although |
| 764 // this sounds like it would clobber an existing resource fork, the Carbon |
| 765 // Resource Manager docs for this function say that's not the case. If |
| 766 // the file already has a resource fork, we receive a result code of |
| 767 // dupFNErr, which is not really an error per se, but just a notification |
| 768 // to us that creating a new resource fork for the file was not necessary.) |
| 769 FSCreateResFile( |
| 770 &parentDirectoryFSRef, |
| 771 filename.length, |
| 772 filename.unicode, |
| 773 kFSCatInfoNone, |
| 774 /*catalogInfo/*/ NULL, |
| 775 &targetFileFSRef, |
| 776 /*newSpec*/ NULL); |
| 777 result = ResError(); |
| 778 if (result == dupFNErr) { |
| 779 // If the call to FSCreateResFile() returned dupFNErr, targetFileFSRef w
ill not have been set, so create it from the path. |
| 780 if (![path getFSRef:&targetFileFSRef createFileIfNecessary:NO]) |
| 781 return NO; |
| 782 } else if (result != noErr) { |
| 783 return NO; |
| 784 } |
| 785 |
| 786 // Open the file's resource fork. |
| 787 file = FSOpenResFile( &targetFileFSRef, fsRdWrPerm ); |
| 788 if (file == -1) |
| 789 return NO; |
| 790 |
| 791 // Make a copy of the icon family data to pass to AddResource(). |
| 792 // (AddResource() takes ownership of the handle we pass in; after the |
| 793 // CloseResFile() call its master pointer will be set to 0xffffffff. |
| 794 // We want to keep the icon family data, so we make a copy.) |
| 795 // HandToHand() returns the handle of the copy in hIconFamily. |
| 796 hIconFamilyCopy = (Handle) hIconFamily; |
| 797 result = HandToHand( &hIconFamilyCopy ); |
| 798 if (result != noErr) { |
| 799 CloseResFile( file ); |
| 800 return NO; |
| 801 } |
| 802 |
| 803 // Remove the file's existing kCustomIconResource of type kIconFamilyType |
| 804 // (if any). |
| 805 hExistingCustomIcon = GetResource( kIconFamilyType, kCustomIconResource ); |
| 806 if( hExistingCustomIcon ) |
| 807 RemoveResource( hExistingCustomIcon ); |
| 808 |
| 809 // Now add our icon family as the file's new custom icon. |
| 810 AddResource( (Handle)hIconFamilyCopy, kIconFamilyType, |
| 811 kCustomIconResource, "\p"); |
| 812 if (ResError() != noErr) { |
| 813 CloseResFile( file ); |
| 814 return NO; |
| 815 } |
| 816 |
| 817 if( compat ) |
| 818 { |
| 819 [self addResourceType:kLarge8BitData asResID:kCustomIconResource]; |
| 820 [self addResourceType:kLarge1BitMask asResID:kCustomIconResource]; |
| 821 [self addResourceType:kSmall8BitData asResID:kCustomIconResource]; |
| 822 [self addResourceType:kSmall1BitMask asResID:kCustomIconResource]; |
| 823 } |
| 824 |
| 825 // Close the file's resource fork, flushing the resource map and new icon |
| 826 // data out to disk. |
| 827 CloseResFile( file ); |
| 828 if (ResError() != noErr) |
| 829 return NO; |
| 830 |
| 831 // Prepare to get the Finder info. |
| 832 |
| 833 // Now we need to set the file's Finder info so the Finder will know that |
| 834 // it has a custom icon. Start by getting the file's current finder info: |
| 835 result = FSGetCatalogInfo( |
| 836 &targetFileFSRef, |
| 837 kFSCatInfoFinderInfo, |
| 838 &catInfo, |
| 839 /*outName*/ NULL, |
| 840 /*fsSpec*/ NULL, |
| 841 /*parentRef*/ NULL); |
| 842 if (result != noErr) |
| 843 return NO; |
| 844 |
| 845 // Set the kHasCustomIcon flag, and clear the kHasBeenInited flag. |
| 846 // |
| 847 // From Apple's "CustomIcon" code sample: |
| 848 // "set bit 10 (has custom icon) and unset the inited flag |
| 849 // kHasBeenInited is 0x0100 so the mask will be 0xFEFF:" |
| 850 // finderInfo.fdFlags = 0xFEFF & (finderInfo.fdFlags | kHasCustomIcon ) ; |
| 851 finderInfo->finderFlags = (finderInfo->finderFlags | kHasCustomIcon ) & ~kHa
sBeenInited; |
| 852 |
| 853 // Now write the Finder info back. |
| 854 result = FSSetCatalogInfo( &targetFileFSRef, kFSCatInfoFinderInfo, &catInfo
); |
| 855 if (result != noErr) |
| 856 return NO; |
| 857 |
| 858 // Now set the modification time back to when the file was actually last mod
ified. |
| 859 NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:modifi
cationDate, NSFileModificationDate, nil]; |
| 860 [[NSFileManager defaultManager] iconfamily_setAttributes:attributes atPath:p
ath]; |
| 861 |
| 862 // Notify the system that the directory containing the file has changed, to |
| 863 // give Finder the chance to find out about the file's new custom icon. |
| 864 result = FNNotify( &parentDirectoryFSRef, kFNDirectoryModifiedMessage, kNilO
ptions ); |
| 865 if (result != noErr) |
| 866 return NO; |
| 867 |
| 868 return YES; |
| 869 } |
| 870 |
| 871 + (BOOL) removeCustomIconFromFile:(NSString*)path |
| 872 { |
| 873 FSRef targetFileFSRef; |
| 874 FSRef parentDirectoryFSRef; |
| 875 SInt16 file; |
| 876 OSStatus result; |
| 877 struct FSCatalogInfo catInfo; |
| 878 struct FileInfo *finderInfo = (struct FileInfo *)&catInfo.finderInfo; |
| 879 Handle hExistingCustomIcon; |
| 880 |
| 881 // Get an FSRef for the target file. |
| 882 if (![path getFSRef:&targetFileFSRef createFileIfNecessary:NO]) |
| 883 return NO; |
| 884 |
| 885 // Open the file's resource fork, if it has one. |
| 886 file = FSOpenResFile( &targetFileFSRef, fsRdWrPerm ); |
| 887 if (file == -1) |
| 888 return NO; |
| 889 |
| 890 // Remove the file's existing kCustomIconResource of type kIconFamilyType |
| 891 // (if any). |
| 892 hExistingCustomIcon = GetResource( kIconFamilyType, kCustomIconResource ); |
| 893 if( hExistingCustomIcon ) |
| 894 RemoveResource( hExistingCustomIcon ); |
| 895 |
| 896 // Close the file's resource fork, flushing the resource map out to disk. |
| 897 CloseResFile( file ); |
| 898 if (ResError() != noErr) |
| 899 return NO; |
| 900 |
| 901 // Now we need to set the file's Finder info so the Finder will know that |
| 902 // it has no custom icon. Start by getting the file's current Finder info. |
| 903 // Also get an FSRef for its parent directory, that we can use in the |
| 904 // FNNotify() call below. |
| 905 result = FSGetCatalogInfo( |
| 906 &targetFileFSRef, |
| 907 kFSCatInfoFinderInfo, |
| 908 &catInfo, |
| 909 /*outName*/ NULL, |
| 910 /*fsSpec*/ NULL, |
| 911 &parentDirectoryFSRef ); |
| 912 if (result != noErr) |
| 913 return NO; |
| 914 |
| 915 // Clear the kHasCustomIcon flag and the kHasBeenInited flag. |
| 916 finderInfo->finderFlags = finderInfo->finderFlags & ~(kHasCustomIcon | kHasB
eenInited); |
| 917 |
| 918 // Now write the Finder info back. |
| 919 result = FSSetCatalogInfo( &targetFileFSRef, kFSCatInfoFinderInfo, &catInfo
); |
| 920 if (result != noErr) |
| 921 return NO; |
| 922 |
| 923 // 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. |
| 924 result = FNNotify( &parentDirectoryFSRef, kFNDirectoryModifiedMessage, kNilO
ptions ); |
| 925 if (result != noErr) |
| 926 return NO; |
| 927 |
| 928 return YES; |
| 929 } |
| 930 |
| 931 - (BOOL) setAsCustomIconForDirectory:(NSString*)path |
| 932 { |
| 933 return [self setAsCustomIconForDirectory:path withCompatibility:NO]; |
| 934 } |
| 935 |
| 936 - (BOOL) setAsCustomIconForDirectory:(NSString*)path withCompatibility:(BOOL)com
pat |
| 937 { |
| 938 NSFileManager *fm = [NSFileManager defaultManager]; |
| 939 BOOL isDir; |
| 940 BOOL exists; |
| 941 NSString *iconrPath; |
| 942 FSRef targetFolderFSRef, iconrFSRef; |
| 943 SInt16 file; |
| 944 OSErr result; |
| 945 struct HFSUniStr255 filename; |
| 946 struct FSCatalogInfo catInfo; |
| 947 Handle hExistingCustomIcon; |
| 948 Handle hIconFamilyCopy; |
| 949 |
| 950 // Confirm that "path" exists and specifies a directory. |
| 951 exists = [fm fileExistsAtPath:path isDirectory:&isDir]; |
| 952 if( !isDir || !exists ) |
| 953 return NO; |
| 954 |
| 955 // Get an FSRef for the folder. |
| 956 if( ![path getFSRef:&targetFolderFSRef createFileIfNecessary:NO] ) |
| 957 return NO; |
| 958 |
| 959 // Remove and re-create any existing "Icon\r" file in the directory, and get
an FSRef for it. |
| 960 iconrPath = [path stringByAppendingPathComponent:@"Icon\r"]; |
| 961 if( [fm fileExistsAtPath:iconrPath] ) |
| 962 { |
| 963 if( ![fm iconfamily_removeItemAtPath:iconrPath] ) |
| 964 return NO; |
| 965 } |
| 966 if( ![iconrPath getFSRef:&iconrFSRef createFileIfNecessary:YES] ) |
| 967 return NO; |
| 968 |
| 969 // Get type and creator information for the Icon file. |
| 970 result = FSGetCatalogInfo( |
| 971 &iconrFSRef, |
| 972 kFSCatInfoFinderInfo, |
| 973 &catInfo, |
| 974 /*outName*/ NULL, |
| 975 /*fsSpec*/ NULL, |
| 976 /*parentRef*/ NULL ); |
| 977 // This shouldn't fail because we just created the file above. |
| 978 if( result != noErr ) |
| 979 return NO; |
| 980 else { |
| 981 // The file doesn't exist. Prepare to create it. |
| 982 struct FileInfo *finderInfo = (struct FileInfo *)catInfo.finderInfo; |
| 983 |
| 984 // These are the file type and creator given to Icon files created by |
| 985 // the Finder. |
| 986 finderInfo->fileType = 'icon'; |
| 987 finderInfo->fileCreator = 'MACS'; |
| 988 |
| 989 // Icon files should be invisible. |
| 990 finderInfo->finderFlags = kIsInvisible; |
| 991 |
| 992 // Because the inited flag is not set in finderFlags above, the Finder |
| 993 // will ignore the location, unless it's in the 'magic rectangle' of |
| 994 // { -24,000, -24,000, -16,000, -16,000 } (technote TB42). |
| 995 // So we need to make sure to set this to zero anyway, so that the |
| 996 // Finder will position it automatically. If the user makes the Icon |
| 997 // file visible for any reason, we don't want it to be positioned in an |
| 998 // exotic corner of the window. |
| 999 finderInfo->location.h = finderInfo->location.v = 0; |
| 1000 |
| 1001 // Standard reserved-field practice. |
| 1002 finderInfo->reservedField = 0; |
| 1003 |
| 1004 // Update the catalog info: |
| 1005 result = FSSetCatalogInfo(&iconrFSRef, kFSCatInfoFinderInfo, &catInfo); |
| 1006 |
| 1007 if (result != noErr) |
| 1008 return NO; |
| 1009 } |
| 1010 |
| 1011 // Get the filename, to be applied to the Icon file. |
| 1012 filename.length = [@"Icon\r" length]; |
| 1013 [@"Icon\r" getCharacters:filename.unicode]; |
| 1014 |
| 1015 // Make sure the file has a resource fork that we can open. (Although |
| 1016 // this sounds like it would clobber an existing resource fork, the Carbon |
| 1017 // Resource Manager docs for this function say that's not the case.) |
| 1018 FSCreateResFile( |
| 1019 &targetFolderFSRef, |
| 1020 filename.length, |
| 1021 filename.unicode, |
| 1022 kFSCatInfoFinderInfo, |
| 1023 &catInfo, |
| 1024 &iconrFSRef, |
| 1025 /*newSpec*/ NULL); |
| 1026 result = ResError(); |
| 1027 if (!(result == noErr || result == dupFNErr)) |
| 1028 return NO; |
| 1029 |
| 1030 // Open the file's resource fork. |
| 1031 file = FSOpenResFile( &iconrFSRef, fsRdWrPerm ); |
| 1032 if (file == -1) |
| 1033 return NO; |
| 1034 |
| 1035 // Make a copy of the icon family data to pass to AddResource(). |
| 1036 // (AddResource() takes ownership of the handle we pass in; after the |
| 1037 // CloseResFile() call its master pointer will be set to 0xffffffff. |
| 1038 // We want to keep the icon family data, so we make a copy.) |
| 1039 // HandToHand() returns the handle of the copy in hIconFamily. |
| 1040 hIconFamilyCopy = (Handle) hIconFamily; |
| 1041 result = HandToHand( &hIconFamilyCopy ); |
| 1042 if (result != noErr) { |
| 1043 CloseResFile( file ); |
| 1044 return NO; |
| 1045 } |
| 1046 |
| 1047 // Remove the file's existing kCustomIconResource of type kIconFamilyType |
| 1048 // (if any). |
| 1049 hExistingCustomIcon = GetResource( kIconFamilyType, kCustomIconResource ); |
| 1050 if( hExistingCustomIcon ) |
| 1051 RemoveResource( hExistingCustomIcon ); |
| 1052 |
| 1053 // Now add our icon family as the file's new custom icon. |
| 1054 AddResource( (Handle)hIconFamilyCopy, kIconFamilyType, |
| 1055 kCustomIconResource, "\p"); |
| 1056 |
| 1057 if (ResError() != noErr) { |
| 1058 CloseResFile( file ); |
| 1059 return NO; |
| 1060 } |
| 1061 |
| 1062 if( compat ) |
| 1063 { |
| 1064 [self addResourceType:kLarge8BitData asResID:kCustomIconResource]; |
| 1065 [self addResourceType:kLarge1BitMask asResID:kCustomIconResource]; |
| 1066 [self addResourceType:kSmall8BitData asResID:kCustomIconResource]; |
| 1067 [self addResourceType:kSmall1BitMask asResID:kCustomIconResource]; |
| 1068 } |
| 1069 |
| 1070 // Close the file's resource fork, flushing the resource map and new icon |
| 1071 // data out to disk. |
| 1072 CloseResFile( file ); |
| 1073 if (ResError() != noErr) |
| 1074 return NO; |
| 1075 |
| 1076 result = FSGetCatalogInfo( &targetFolderFSRef, |
| 1077 kFSCatInfoFinderInfo, |
| 1078 &catInfo, |
| 1079 /*outName*/ NULL, |
| 1080 /*fsSpec*/ NULL, |
| 1081 /*parentRef*/ NULL); |
| 1082 if( result != noErr ) |
| 1083 return NO; |
| 1084 |
| 1085 // Tell the Finder that the folder now has a custom icon. |
| 1086 ((struct FolderInfo *)catInfo.finderInfo)->finderFlags = ( ((struct FolderIn
fo *)catInfo.finderInfo)->finderFlags | kHasCustomIcon ) & ~kHasBeenInited; |
| 1087 |
| 1088 result = FSSetCatalogInfo( &targetFolderFSRef, |
| 1089 kFSCatInfoFinderInfo, |
| 1090 &catInfo); |
| 1091 if( result != noErr ) |
| 1092 return NO; |
| 1093 |
| 1094 // Notify the system that the target directory has changed, to give Finder |
| 1095 // the chance to find out about its new custom icon. |
| 1096 result = FNNotify( &targetFolderFSRef, kFNDirectoryModifiedMessage, kNilOpti
ons ); |
| 1097 if (result != noErr) |
| 1098 return NO; |
| 1099 |
| 1100 return YES; |
| 1101 } |
| 1102 |
| 1103 + (BOOL) removeCustomIconFromDirectory:(NSString*)path |
| 1104 { |
| 1105 FSRef targetFolderFSRef; |
| 1106 if( [path getFSRef:&targetFolderFSRef createFileIfNecessary:NO] ) { |
| 1107 OSStatus result; |
| 1108 struct FSCatalogInfo catInfo; |
| 1109 struct FileInfo *finderInfo = (struct FileInfo *)catInfo.finderInfo; |
| 1110 |
| 1111 result = FSGetCatalogInfo( &targetFolderFSRef, |
| 1112 kFSCatInfoFinderInfo, |
| 1113 &catInfo, |
| 1114 /*outName*/ NULL, |
| 1115 /*fsSpec*/ NULL, |
| 1116 /*parentRef*/ NULL); |
| 1117 if( result != noErr ) |
| 1118 return NO; |
| 1119 |
| 1120 // Tell the Finder that the folder no longer has a custom icon. |
| 1121 finderInfo->finderFlags &= ~( kHasCustomIcon | kHasBeenInited ); |
| 1122 |
| 1123 result = FSSetCatalogInfo( &targetFolderFSRef, |
| 1124 kFSCatInfoFinderInfo, |
| 1125 &catInfo); |
| 1126 if( result != noErr ) |
| 1127 return NO; |
| 1128 |
| 1129 // Notify the system that the target directory has changed, to give Find
er |
| 1130 // the chance to find out about its new custom icon. |
| 1131 result = FNNotify( &targetFolderFSRef, kFNDirectoryModifiedMessage, kNil
Options ); |
| 1132 if (result != noErr) |
| 1133 return NO; |
| 1134 } |
| 1135 |
| 1136 if( ! [[NSFileManager defaultManager] iconfamily_removeItemAtPath:[path stri
ngByAppendingPathComponent:@"Icon\r"]] ) |
| 1137 return NO; |
| 1138 |
| 1139 return YES; |
| 1140 } |
| 1141 |
| 1142 - (NSData *) data |
| 1143 { |
| 1144 return [NSData dataWithBytes:*hIconFamily length:GetHandleSize((Handle)hIcon
Family)]; |
| 1145 } |
| 1146 |
| 1147 - (BOOL) writeToFile:(NSString*)path |
| 1148 { |
| 1149 return [[self data] writeToFile:path atomically:NO]; |
| 1150 } |
| 1151 |
| 1152 @end |
| 1153 |
| 1154 @implementation IconFamily (Internals) |
| 1155 |
| 1156 + (NSImage*) resampleImage:(NSImage*)image toIconWidth:(int)iconWidth usingImage
Interpolation:(NSImageInterpolation)imageInterpolation |
| 1157 { |
| 1158 NSGraphicsContext* graphicsContext; |
| 1159 BOOL wasAntialiasing; |
| 1160 NSImageInterpolation previousImageInterpolation; |
| 1161 NSImage* newImage; |
| 1162 NSImage* workingImage; |
| 1163 NSImageRep* workingImageRep; |
| 1164 NSSize size, pixelSize, newSize; |
| 1165 NSRect iconRect; |
| 1166 NSRect targetRect; |
| 1167 |
| 1168 // Create a working copy of the image and scale its size down to fit in |
| 1169 // the square area of the icon. |
| 1170 // |
| 1171 // It seems like there should be a more memory-efficient alternative to |
| 1172 // first duplicating the entire original image, but I don't know what it |
| 1173 // is. We need to change some properties ("size" and "scalesWhenResized") |
| 1174 // of the original image, but we shouldn't change the original, so a copy |
| 1175 // is necessary. |
| 1176 workingImage = [image copyWithZone:[image zone]]; |
| 1177 [workingImage setScalesWhenResized:YES]; |
| 1178 size = [workingImage size]; |
| 1179 workingImageRep = [workingImage iconfamily_bestRepresentation]; |
| 1180 if ([workingImageRep isKindOfClass:[NSBitmapImageRep class]]) { |
| 1181 pixelSize.width = [workingImageRep pixelsWide]; |
| 1182 pixelSize.height = [workingImageRep pixelsHigh]; |
| 1183 if (!NSEqualSizes( size, pixelSize )) { |
| 1184 [workingImage setSize:pixelSize]; |
| 1185 [workingImageRep setSize:pixelSize]; |
| 1186 size = pixelSize; |
| 1187 } |
| 1188 } |
| 1189 if (size.width >= size.height) { |
| 1190 newSize.width = iconWidth; |
| 1191 newSize.height = (float)floor( iconWidth * size.height / size.width + 0.
5 ); |
| 1192 } else { |
| 1193 newSize.height = iconWidth; |
| 1194 newSize.width = (float)floor( iconWidth * size.width / size.height + 0.
5 ); |
| 1195 } |
| 1196 [workingImage setSize:newSize]; |
| 1197 |
| 1198 // Create a new image the size of the icon, and clear it to transparent. |
| 1199 newImage = [[NSImage alloc] initWithSize:NSMakeSize(iconWidth,iconWidth)]; |
| 1200 [newImage lockFocus]; |
| 1201 iconRect.origin.x = iconRect.origin.y = 0; |
| 1202 iconRect.size.width = iconRect.size.height = iconWidth; |
| 1203 [[NSColor clearColor] set]; |
| 1204 NSRectFill( iconRect ); |
| 1205 |
| 1206 // Set current graphics context to use antialiasing and high-quality |
| 1207 // image scaling. |
| 1208 graphicsContext = [NSGraphicsContext currentContext]; |
| 1209 wasAntialiasing = [graphicsContext shouldAntialias]; |
| 1210 previousImageInterpolation = [graphicsContext imageInterpolation]; |
| 1211 [graphicsContext setShouldAntialias:YES]; |
| 1212 [graphicsContext setImageInterpolation:imageInterpolation]; |
| 1213 |
| 1214 // Composite the working image into the icon bitmap, centered. |
| 1215 targetRect.origin.x = ((float)iconWidth - newSize.width ) / 2.0f; |
| 1216 targetRect.origin.y = ((float)iconWidth - newSize.height) / 2.0f; |
| 1217 targetRect.size.width = newSize.width; |
| 1218 targetRect.size.height = newSize.height; |
| 1219 [workingImageRep drawInRect:targetRect]; |
| 1220 |
| 1221 // Restore previous graphics context settings. |
| 1222 [graphicsContext setShouldAntialias:wasAntialiasing]; |
| 1223 [graphicsContext setImageInterpolation:previousImageInterpolation]; |
| 1224 |
| 1225 [newImage unlockFocus]; |
| 1226 |
| 1227 [workingImage release]; |
| 1228 |
| 1229 // Return the new image! |
| 1230 return [newImage autorelease]; |
| 1231 } |
| 1232 |
| 1233 + (Handle) get32BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requ
iredPixelSize:(int)requiredPixelSize |
| 1234 { |
| 1235 Handle hRawData; |
| 1236 unsigned char* pRawData; |
| 1237 Size rawDataSize; |
| 1238 unsigned char* pSrc; |
| 1239 unsigned char* pDest; |
| 1240 int x, y; |
| 1241 unsigned char alphaByte; |
| 1242 float oneOverAlpha; |
| 1243 |
| 1244 // Get information about the bitmapImageRep. |
| 1245 long pixelsWide = [bitmapImageRep pixelsWide]; |
| 1246 long pixelsHigh = [bitmapImageRep pixelsHigh]; |
| 1247 long bitsPerSample = [bitmapImageRep bitsPerSample]; |
| 1248 long samplesPerPixel = [bitmapImageRep samplesPerPixel]; |
| 1249 long bitsPerPixel = [bitmapImageRep bitsPerPixel]; |
| 1250 BOOL isPlanar = [bitmapImageRep isPlanar]; |
| 1251 long bytesPerRow = [bitmapImageRep bytesPerRow]; |
| 1252 unsigned char* bitmapData = [bitmapImageRep bitmapData]; |
| 1253 |
| 1254 // Make sure bitmap has the required dimensions. |
| 1255 if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize) |
| 1256 return NULL; |
| 1257 |
| 1258 // So far, this code only handles non-planar 32-bit RGBA and 24-bit RGB sour
ce bitmaps. |
| 1259 // This could be made more flexible with some additional programming to acco
mmodate other possible |
| 1260 // formats... |
| 1261 if (isPlanar) |
| 1262 { |
| 1263 NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: return
ing NULL due to isPlanar == YES"); |
| 1264 return NULL; |
| 1265 } |
| 1266 if (bitsPerSample != 8) |
| 1267 { |
| 1268 NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: return
ing NULL due to bitsPerSample == %ld", bitsPerSample); |
| 1269 return NULL; |
| 1270 } |
| 1271 |
| 1272 if (((samplesPerPixel == 3) && (bitsPerPixel == 24)) || ((samplesPerPixe
l == 4) && (bitsPerPixel == 32))) |
| 1273 { |
| 1274 rawDataSize = pixelsWide * pixelsHigh * 4; |
| 1275 hRawData = NewHandle( rawDataSize ); |
| 1276 if (hRawData == NULL) |
| 1277 return NULL; |
| 1278 pRawData = (unsigned char*) *hRawData; |
| 1279 |
| 1280 pDest = pRawData; |
| 1281 |
| 1282 if (bitsPerPixel == 32) { |
| 1283 for (y = 0; y < pixelsHigh; y++) { |
| 1284 pSrc = bitmapData + y * bytesPerRow; |
| 1285 for (x = 0; x < pixelsWide; x++) { |
| 1286 // Each pixel is 3 bytes of RGB
data, followed by 1 byte of |
| 1287 // alpha. The RGB values are pr
emultiplied by the alpha (so |
| 1288 // that Quartz can save time whe
n compositing the bitmap to a |
| 1289 // destination), and we undo thi
s premultiplication (with some |
| 1290 // lossiness unfortunately) when
retrieving the bitmap data. |
| 1291 *pDest++ = alphaByte = *(pSrc+3)
; |
| 1292 if (alphaByte) { |
| 1293 oneOverAlpha = 255.0f /
(float)alphaByte; |
| 1294 *pDest++ = *(pSrc+0) * o
neOverAlpha; |
| 1295 *pDest++ = *(pSrc+1) * o
neOverAlpha; |
| 1296 *pDest++ = *(pSrc+2) * o
neOverAlpha; |
| 1297 } else { |
| 1298 *pDest++ = 0; |
| 1299 *pDest++ = 0; |
| 1300 *pDest++ = 0; |
| 1301 } |
| 1302 pSrc+=4; |
| 1303 } |
| 1304 } |
| 1305 } else if (bitsPerPixel == 24) { |
| 1306 for (y = 0; y < pixelsHigh; y++) { |
| 1307 pSrc = bitmapData + y * bytesPerRow; |
| 1308 for (x = 0; x < pixelsWide; x++) { |
| 1309 *pDest++ = 0xFF; |
| 1310 *pDest++ = *pSrc++; |
| 1311 *pDest++ = *pSrc++; |
| 1312 *pDest++ = *pSrc++; |
| 1313 } |
| 1314 } |
| 1315 } |
| 1316 } |
| 1317 else |
| 1318 { |
| 1319 NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: return
ing NULL due to samplesPerPixel == %ld, bitsPerPixel == %ld", samplesPerPixel, b
itsPerPixel); |
| 1320 return NULL; |
| 1321 } |
| 1322 |
| 1323 return hRawData; |
| 1324 } |
| 1325 |
| 1326 + (Handle) get8BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requi
redPixelSize:(int)requiredPixelSize |
| 1327 { |
| 1328 Handle hRawData; |
| 1329 unsigned char* pRawData; |
| 1330 Size rawDataSize; |
| 1331 unsigned char* pSrc; |
| 1332 unsigned char* pDest; |
| 1333 int x, y; |
| 1334 |
| 1335 // Get information about the bitmapImageRep. |
| 1336 long pixelsWide = [bitmapImageRep pixelsWide]; |
| 1337 long pixelsHigh = [bitmapImageRep pixelsHigh]; |
| 1338 long bitsPerSample = [bitmapImageRep bitsPerSample]; |
| 1339 long samplesPerPixel = [bitmapImageRep samplesPerPixel]; |
| 1340 long bitsPerPixel = [bitmapImageRep bitsPerPixel]; |
| 1341 BOOL isPlanar = [bitmapImageRep isPlanar]; |
| 1342 long bytesPerRow = [bitmapImageRep bytesPerRow]; |
| 1343 unsigned char* bitmapData = [bitmapImageRep bitmapData]; |
| 1344 |
| 1345 // Make sure bitmap has the required dimensions. |
| 1346 if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize) |
| 1347 return NULL; |
| 1348 |
| 1349 // So far, this code only handles non-planar 32-bit RGBA and 24-bit RGB sour
ce bitmaps. |
| 1350 // This could be made more flexible with some additional programming... |
| 1351 if (isPlanar) |
| 1352 { |
| 1353 NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returni
ng NULL due to isPlanar == YES"); |
| 1354 return NULL; |
| 1355 } |
| 1356 if (bitsPerSample != 8) |
| 1357 { |
| 1358 NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returni
ng NULL due to bitsPerSample == %ld", bitsPerSample); |
| 1359 return NULL; |
| 1360 } |
| 1361 |
| 1362 if (((samplesPerPixel == 3) && (bitsPerPixel == 24)) || ((samplesPerPixe
l == 4) && (bitsPerPixel == 32))) |
| 1363 { |
| 1364 CGDirectPaletteRef cgPal; |
| 1365 CGDeviceColor cgCol; |
| 1366 |
| 1367 rawDataSize = pixelsWide * pixelsHigh; |
| 1368 hRawData = NewHandle( rawDataSize ); |
| 1369 if (hRawData == NULL) |
| 1370 return NULL; |
| 1371 pRawData = (unsigned char*) *hRawData; |
| 1372 |
| 1373 cgPal = CGPaletteCreateDefaultColorPalette(); |
| 1374 |
| 1375 pDest = pRawData; |
| 1376 if (bitsPerPixel == 32) { |
| 1377 for (y = 0; y < pixelsHigh; y++) { |
| 1378 pSrc = bitmapData + y * bytesPerRow; |
| 1379 for (x = 0; x < pixelsWide; x++) { |
| 1380 cgCol.red = ((float)*(pSrc)) / 255; |
| 1381 cgCol.green = ((float)*(pSrc+1)) / 255; |
| 1382 cgCol.blue = ((float)*(pSrc+2)) / 255; |
| 1383 |
| 1384 *pDest++ = CGPaletteGetIndexForColor(cgP
al, cgCol); |
| 1385 |
| 1386 pSrc+=4; |
| 1387 } |
| 1388 } |
| 1389 } else if (bitsPerPixel == 24) { |
| 1390 for (y = 0; y < pixelsHigh; y++) { |
| 1391 pSrc = bitmapData + y * bytesPerRow; |
| 1392 for (x = 0; x < pixelsWide; x++) { |
| 1393 cgCol.red = ((float)*(pSrc)) / 255; |
| 1394 cgCol.green = ((float)*(pSrc+1)) / 255; |
| 1395 cgCol.blue = ((float)*(pSrc+2)) / 255; |
| 1396 |
| 1397 *pDest++ = CGPaletteGetIndexForColor(cgP
al, cgCol); |
| 1398 |
| 1399 pSrc+=3; |
| 1400 } |
| 1401 } |
| 1402 } |
| 1403 |
| 1404 CGPaletteRelease(cgPal); |
| 1405 } |
| 1406 else |
| 1407 { |
| 1408 NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returni
ng NULL due to samplesPerPixel == %ld, bitsPerPixel == %ld", samplesPerPixel, bi
tsPerPixel); |
| 1409 return NULL; |
| 1410 } |
| 1411 |
| 1412 return hRawData; |
| 1413 } |
| 1414 |
| 1415 + (Handle) get8BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requi
redPixelSize:(int)requiredPixelSize |
| 1416 { |
| 1417 Handle hRawData; |
| 1418 unsigned char* pRawData; |
| 1419 Size rawDataSize; |
| 1420 unsigned char* pSrc; |
| 1421 unsigned char* pDest; |
| 1422 int x, y; |
| 1423 |
| 1424 // Get information about the bitmapImageRep. |
| 1425 long pixelsWide = [bitmapImageRep pixelsWide]; |
| 1426 long pixelsHigh = [bitmapImageRep pixelsHigh]; |
| 1427 long bitsPerSample = [bitmapImageRep bitsPerSample]; |
| 1428 long samplesPerPixel = [bitmapImageRep samplesPerPixel]; |
| 1429 long bitsPerPixel = [bitmapImageRep bitsPerPixel]; |
| 1430 BOOL isPlanar = [bitmapImageRep isPlanar]; |
| 1431 long bytesPerRow = [bitmapImageRep bytesPerRow]; |
| 1432 unsigned char* bitmapData = [bitmapImageRep bitmapData]; |
| 1433 |
| 1434 // Make sure bitmap has the required dimensions. |
| 1435 if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize) |
| 1436 return NULL; |
| 1437 |
| 1438 // So far, this code only handles non-planar 32-bit RGBA, 24-bit RGB and 8-b
it grayscale source bitmaps. |
| 1439 // This could be made more flexible with some additional programming... |
| 1440 if (isPlanar) |
| 1441 { |
| 1442 NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returni
ng NULL due to isPlanar == YES"); |
| 1443 return NULL; |
| 1444 } |
| 1445 if (bitsPerSample != 8) |
| 1446 { |
| 1447 NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returni
ng NULL due to bitsPerSample == %ld", bitsPerSample); |
| 1448 return NULL; |
| 1449 } |
| 1450 |
| 1451 if (((samplesPerPixel == 1) && (bitsPerPixel == 8)) || ((samplesPerPixel
== 3) && (bitsPerPixel == 24)) || ((samplesPerPixel == 4) && (bitsPerPixel == 3
2))) |
| 1452 { |
| 1453 rawDataSize = pixelsWide * pixelsHigh; |
| 1454 hRawData = NewHandle( rawDataSize ); |
| 1455 if (hRawData == NULL) |
| 1456 return NULL; |
| 1457 pRawData = (unsigned char*) *hRawData; |
| 1458 |
| 1459 pSrc = bitmapData; |
| 1460 pDest = pRawData; |
| 1461 |
| 1462 if (bitsPerPixel == 32) { |
| 1463 for (y = 0; y < pixelsHigh; y++) { |
| 1464 pSrc = bitmapData + y * bytesPerRow; |
| 1465 for (x = 0; x < pixelsWide; x++) { |
| 1466 pSrc += 3; |
| 1467 *pDest++ = *pSrc++; |
| 1468 } |
| 1469 } |
| 1470 } |
| 1471 else if (bitsPerPixel == 24) { |
| 1472 memset( pDest, 255, rawDataSize ); |
| 1473 } |
| 1474 else if (bitsPerPixel == 8) { |
| 1475 for (y = 0; y < pixelsHigh; y++) { |
| 1476 memcpy( pDest, pSrc, pixelsWide ); |
| 1477 pSrc += bytesPerRow; |
| 1478 pDest += pixelsWide; |
| 1479 } |
| 1480 } |
| 1481 } |
| 1482 else |
| 1483 { |
| 1484 NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returni
ng NULL due to samplesPerPixel == %ld, bitsPerPixel == %ld", samplesPerPixel, bi
tsPerPixel); |
| 1485 return NULL; |
| 1486 } |
| 1487 |
| 1488 return hRawData; |
| 1489 } |
| 1490 |
| 1491 // NOTE: This method hasn't been fully tested yet. |
| 1492 + (Handle) get1BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requi
redPixelSize:(int)requiredPixelSize |
| 1493 { |
| 1494 Handle hRawData; |
| 1495 unsigned char* pRawData; |
| 1496 Size rawDataSize; |
| 1497 unsigned char* pSrc; |
| 1498 unsigned char* pDest; |
| 1499 int x, y; |
| 1500 unsigned char maskByte; |
| 1501 |
| 1502 // Get information about the bitmapImageRep. |
| 1503 long pixelsWide = [bitmapImageRep pixelsWide]; |
| 1504 long pixelsHigh = [bitmapImageRep pixelsHigh]; |
| 1505 long bitsPerSample = [bitmapImageRep bitsPerSample]; |
| 1506 long samplesPerPixel = [bitmapImageRep samplesPerPixel]; |
| 1507 long bitsPerPixel = [bitmapImageRep bitsPerPixel]; |
| 1508 BOOL isPlanar = [bitmapImageRep isPlanar]; |
| 1509 long bytesPerRow = [bitmapImageRep bytesPerRow]; |
| 1510 unsigned char* bitmapData = [bitmapImageRep bitmapData]; |
| 1511 |
| 1512 // Make sure bitmap has the required dimensions. |
| 1513 if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize) |
| 1514 return NULL; |
| 1515 |
| 1516 // So far, this code only handles non-planar 32-bit RGBA, 24-bit RGB, 8-bit
grayscale, and 1-bit source bitmaps. |
| 1517 // This could be made more flexible with some additional programming... |
| 1518 if (isPlanar) |
| 1519 { |
| 1520 NSLog(@"get1BitMaskFromBitmapImageRep:requiredPixelSize: returni
ng NULL due to isPlanar == YES"); |
| 1521 return NULL; |
| 1522 } |
| 1523 |
| 1524 if (((bitsPerPixel == 1) && (samplesPerPixel == 1) && (bitsPerSample ==
1)) || ((bitsPerPixel == 8) && (samplesPerPixel == 1) && (bitsPerSample == 8)) |
| |
| 1525 ((bitsPerPixel == 24) && (samplesPerPixel == 3) && (bitsPerSampl
e == 8)) || ((bitsPerPixel == 32) && (samplesPerPixel == 4) && (bitsPerSample ==
8))) |
| 1526 { |
| 1527 rawDataSize = (pixelsWide * pixelsHigh)/4; |
| 1528 hRawData = NewHandle( rawDataSize ); |
| 1529 if (hRawData == NULL) |
| 1530 return NULL; |
| 1531 pRawData = (unsigned char*) *hRawData; |
| 1532 |
| 1533 pSrc = bitmapData; |
| 1534 pDest = pRawData; |
| 1535 |
| 1536 if (bitsPerPixel == 32) { |
| 1537 for (y = 0; y < pixelsHigh; y++) { |
| 1538 pSrc = bitmapData + y * bytesPerRow; |
| 1539 for (x = 0; x < pixelsWide; x += 8) { |
| 1540 maskByte = 0; |
| 1541 maskByte |= (*(unsigned*)pSrc & 0xff) ?
0x80 : 0; pSrc += 4; |
| 1542 maskByte |= (*(unsigned*)pSrc & 0xff) ?
0x40 : 0; pSrc += 4; |
| 1543 maskByte |= (*(unsigned*)pSrc & 0xff) ?
0x20 : 0; pSrc += 4; |
| 1544 maskByte |= (*(unsigned*)pSrc & 0xff) ?
0x10 : 0; pSrc += 4; |
| 1545 maskByte |= (*(unsigned*)pSrc & 0xff) ?
0x08 : 0; pSrc += 4; |
| 1546 maskByte |= (*(unsigned*)pSrc & 0xff) ?
0x04 : 0; pSrc += 4; |
| 1547 maskByte |= (*(unsigned*)pSrc & 0xff) ?
0x02 : 0; pSrc += 4; |
| 1548 maskByte |= (*(unsigned*)pSrc & 0xff) ?
0x01 : 0; pSrc += 4; |
| 1549 *pDest++ = maskByte; |
| 1550 } |
| 1551 } |
| 1552 } |
| 1553 else if (bitsPerPixel == 24) { |
| 1554 memset( pDest, 255, rawDataSize ); |
| 1555 } |
| 1556 else if (bitsPerPixel == 8) { |
| 1557 for (y = 0; y < pixelsHigh; y++) { |
| 1558 pSrc = bitmapData + y * bytesPerRow; |
| 1559 for (x = 0; x < pixelsWide; x += 8) { |
| 1560 maskByte = 0; |
| 1561 maskByte |= *pSrc++ ? 0x80 : 0; |
| 1562 maskByte |= *pSrc++ ? 0x40 : 0; |
| 1563 maskByte |= *pSrc++ ? 0x20 : 0; |
| 1564 maskByte |= *pSrc++ ? 0x10 : 0; |
| 1565 maskByte |= *pSrc++ ? 0x08 : 0; |
| 1566 maskByte |= *pSrc++ ? 0x04 : 0; |
| 1567 maskByte |= *pSrc++ ? 0x02 : 0; |
| 1568 maskByte |= *pSrc++ ? 0x01 : 0; |
| 1569 *pDest++ = maskByte; |
| 1570 } |
| 1571 } |
| 1572 } |
| 1573 else if (bitsPerPixel == 1) { |
| 1574 for (y = 0; y < pixelsHigh; y++) { |
| 1575 memcpy( pDest, pSrc, pixelsWide / 8 ); |
| 1576 pDest += pixelsWide / 8; |
| 1577 pSrc += bytesPerRow; |
| 1578 } |
| 1579 } |
| 1580 |
| 1581 memcpy( pRawData+(pixelsWide*pixelsHigh)/8, pRawData, (pixelsWid
e*pixelsHigh)/8 ); |
| 1582 } |
| 1583 else |
| 1584 { |
| 1585 NSLog(@"get1BitMaskFromBitmapImageRep:requiredPixelSize: returni
ng NULL due to bitsPerPixel == %ld, samplesPerPixel== %ld, bitsPerSample == %ld"
, bitsPerPixel, samplesPerPixel, bitsPerSample); |
| 1586 return NULL; |
| 1587 } |
| 1588 |
| 1589 return hRawData; |
| 1590 } |
| 1591 |
| 1592 - (BOOL) addResourceType:(OSType)type asResID:(int)resID |
| 1593 { |
| 1594 Handle hIconRes = NewHandle(0); |
| 1595 OSErr err; |
| 1596 |
| 1597 err = GetIconFamilyData( hIconFamily, type, hIconRes ); |
| 1598 |
| 1599 if( !GetHandleSize(hIconRes) || err != noErr ) |
| 1600 return NO; |
| 1601 |
| 1602 AddResource( hIconRes, type, resID, "\p" ); |
| 1603 |
| 1604 return YES; |
| 1605 } |
| 1606 |
| 1607 @end |
| 1608 |
| 1609 // Methods for interfacing with the Cocoa Pasteboard. |
| 1610 |
| 1611 @implementation IconFamily (ScrapAdditions) |
| 1612 |
| 1613 + (BOOL) canInitWithScrap |
| 1614 { |
| 1615 NSArray *types = [[NSPasteboard generalPasteboard] types]; |
| 1616 return [types containsObject:ICONFAMILY_UTI] || [types containsObject:ICONFA
MILY_PBOARD_TYPE]; |
| 1617 } |
| 1618 |
| 1619 + (IconFamily*) iconFamilyWithScrap |
| 1620 { |
| 1621 return [[[IconFamily alloc] initWithScrap] autorelease]; |
| 1622 } |
| 1623 |
| 1624 - initWithScrap |
| 1625 { |
| 1626 NSPasteboard *pboard = [NSPasteboard generalPasteboard]; |
| 1627 |
| 1628 NSData *data = [pboard dataForType:ICONFAMILY_UTI]; |
| 1629 if( !data ) |
| 1630 data = [pboard dataForType:ICONFAMILY_PBOARD_TYPE]; |
| 1631 if( !data ) |
| 1632 { |
| 1633 [self release]; |
| 1634 return nil; |
| 1635 } |
| 1636 |
| 1637 self = [self initWithData:data]; |
| 1638 |
| 1639 return self; |
| 1640 } |
| 1641 |
| 1642 - (BOOL) putOnScrap |
| 1643 { |
| 1644 NSPasteboard *pboard = [NSPasteboard generalPasteboard]; |
| 1645 |
| 1646 [pboard declareTypes:[NSArray arrayWithObjects:ICONFAMILY_UTI, ICONFAMILY_PB
OARD_TYPE, nil] owner:self]; |
| 1647 NSData *data = [self data]; |
| 1648 [pboard setData:data forType:ICONFAMILY_UTI]; |
| 1649 [pboard setData:data forType:ICONFAMILY_PBOARD_TYPE]; |
| 1650 |
| 1651 return YES; |
| 1652 } |
| 1653 |
| 1654 @end |
| 1655 |
| 1656 |
| 1657 @implementation NSFileManager (IconFamilyCompatibility) |
| 1658 |
| 1659 - (NSDictionary *) iconfamily_attributesAtPath:(NSString *)path |
| 1660 { |
| 1661 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 |
| 1662 if ([!self respondsToSelector:@selector(attributesOfItemAtPath:error:)]) |
| 1663 { |
| 1664 return [self fileAttributesAtPath:path traverseLink:NO]; |
| 1665 } |
| 1666 #endif |
| 1667 |
| 1668 return [self attributesOfItemAtPath:path error:NULL]; |
| 1669 } |
| 1670 |
| 1671 |
| 1672 - (BOOL) iconfamily_setAttributes:(NSDictionary *)attributes atPath:(NSString *)
path |
| 1673 { |
| 1674 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 |
| 1675 if ([!self respondsToSelector:@selector(setAttributes:ofItemAtPath:error
:)]) |
| 1676 { |
| 1677 return [self changeFileAttributes:attributes atPath:path]; |
| 1678 } |
| 1679 #endif |
| 1680 |
| 1681 return [self setAttributes:attributes ofItemAtPath:path error:NULL]; |
| 1682 } |
| 1683 |
| 1684 |
| 1685 - (BOOL) iconfamily_removeItemAtPath:(NSString *)path |
| 1686 { |
| 1687 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 |
| 1688 if ([!self respondsToSelector:@selector(removeItemAtPath:error:)]) |
| 1689 { |
| 1690 return [self removeFileAtPath:path handler:nil]; |
| 1691 } |
| 1692 #endif |
| 1693 |
| 1694 return [self removeItemAtPath:path error:NULL]; |
| 1695 } |
| 1696 |
| 1697 @end |
| 1698 |
| 1699 |
| 1700 @implementation NSImage (IconFamilyCompatibility) |
| 1701 |
| 1702 - (NSImageRep *) iconfamily_bestRepresentation |
| 1703 { |
| 1704 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 |
| 1705 if ([!self respondsToSelector:@selector(bestRepresentationForRect:contex
t:hints:)]) |
| 1706 { |
| 1707 return [self bestRepresentationForDevice:nil]; |
| 1708 } |
| 1709 #endif |
| 1710 |
| 1711 return [self bestRepresentationForRect:(NSRect){NSZeroPoint, [self size]
} context:nil hints:nil]; |
| 1712 } |
| 1713 |
| 1714 @end |
OLD | NEW |