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