/XeePhotoshopLayerParser.m
Objective C | 531 lines | 413 code | 85 blank | 33 comment | 44 complexity | 8300391dba872eb816779e1a6b51a5c4 MD5 | raw file
1#import "XeePhotoshopLayerParser.h" 2#import "XeePhotoshopLoader.h" 3#import "XeeProperties.h" 4#import "XeeTypes.h" 5 6#import "XeeBitmapRawImage.h" 7#import "XeeIndexedRawImage.h" 8#import "XeeRawImage.h" 9 10#import "XeeInterleavingHandle.h" 11 12#import <XADMaster/CSZlibHandle.h> 13 14/*static int XeeOffsetOfStringInMemory(const char *str,const void *mem,int len) 15{ 16 int slen=strlen(str); 17 const char *memstr=mem; 18 for(int i=0;i<=len-slen;i++) 19 { 20 if(str[0]==memstr[i]) 21 { 22 for(int j=1;j<slen;j++) 23 { 24 if(str[j]!=memstr[i+j]) goto end; 25 } 26 return i; 27 } 28 end: 0; 29 } 30 return -1; 31}*/ 32 33@implementation XeePhotoshopLayerParser 34 35+(NSArray *)parseLayersFromHandle:(CSHandle *)fh parentImage:(XeePhotoshopImage *)parent alphaFlag:(BOOL *)hasalpha 36{ 37 NSMutableArray *layers=[NSMutableArray array]; 38 int numlayers=[fh readInt16BE]; 39 40 if(numlayers<0) 41 { 42 if(hasalpha) *hasalpha=YES; 43 numlayers=-numlayers; 44 } 45 46 for(int i=0;i<numlayers;i++) 47 { 48 XeePhotoshopLayerParser *layer=[[[self alloc] initWithHandle:fh parentImage:parent] autorelease]; 49 [layers addObject:layer]; 50 } 51 52 off_t offset=[fh offsetInFile]; 53 NSEnumerator *enumerator=[layers objectEnumerator]; 54 XeePhotoshopLayerParser *layer; 55 while(layer=[enumerator nextObject]) 56 { 57 [layer setDataOffset:offset]; 58 offset+=[layer totalSize]; 59 } 60 61 return layers; 62} 63 64-(id)initWithHandle:(CSHandle *)fh parentImage:(XeePhotoshopImage *)parentimage 65{ 66 if(self=[super init]) 67 { 68 handle=[fh retain]; 69 parent=parentimage; 70 props=[NSMutableArray new]; 71 channeloffs=[NSMutableDictionary new]; 72 73 mode=[parent mode]; 74 depth=[parent bitDepth]; 75 76 int top=[fh readInt32BE]; 77 int left=[fh readInt32BE]; 78 int bottom=[fh readInt32BE]; 79 int right=[fh readInt32BE]; 80 81 width=right-left; 82 height=bottom-top; 83 84 channels=[fh readUInt16BE]; 85 totalsize=0; 86 87 for(int j=0;j<channels;j++) 88 { 89 int channelid=[fh readInt16BE]; 90 uint32_t channellength=[fh readUInt32BE]; 91 92 [channeloffs setObject:[NSNumber numberWithLongLong:totalsize] forKey:[NSNumber numberWithInt:channelid]]; 93 totalsize+=channellength; 94 } 95 96 [fh skipBytes:4]; // '8BIM' 97 uint32_t blendmode=[fh readUInt32BE]; 98 int opacity=[fh readUInt8]; 99 /*int clipping=*/[fh readUInt8]; 100 /*int flags=*/[fh readUInt8]; 101 [fh skipBytes:1]; 102 103 uint32_t extralen=[fh readUInt32BE]; 104 off_t nextoffs=[fh offsetInFile]+extralen; 105 106 int bytesleft=extralen; 107 108 NSString *layername=nil; 109 110 if(bytesleft<4) goto outofbytes; 111 uint32_t masksize=[fh readUInt32BE]; bytesleft-=4; 112 if(bytesleft<masksize) goto outofbytes; 113 [fh skipBytes:masksize]; bytesleft-=masksize; 114 115 if(bytesleft<4) goto outofbytes; 116 uint32_t blendsize=[fh readUInt32BE]; bytesleft-=4; 117 if(bytesleft<blendsize) goto outofbytes; 118 [fh skipBytes:blendsize]; bytesleft-=blendsize; 119 120 if(bytesleft<1) goto outofbytes; 121 int namelen=[fh readUInt8]; bytesleft-=1; 122 if(bytesleft<namelen) goto outofbytes; 123 if(namelen) layername=[[[NSString alloc] initWithData:[fh readDataOfLength:namelen] encoding:NSISOLatin1StringEncoding] autorelease]; 124 int padbytes=((3-namelen)&3); 125 if(bytesleft<padbytes) goto outofbytes; 126 [fh skipBytes:padbytes]; bytesleft-=padbytes; 127 128 NSString *adjustmentname=nil; 129 NSArray *typetoolfonts=nil,*typetooltext=nil; 130 131 while(bytesleft>=12) 132 { 133 uint32_t signature=[fh readUInt32BE]; 134 if(signature!='8BIM') break; 135 uint32_t key=[fh readUInt32BE]; 136 uint32_t chunklen=[fh readUInt32BE]; 137 off_t nextchunk=[fh offsetInFile]+chunklen; 138 bytesleft-=12; 139 if(chunklen>bytesleft) break; 140 141 switch(key) 142 { 143 case 'levl': // levels adjustment layer 144 adjustmentname=NSLocalizedString(@"Levels",@"Photoshop levels adjustment layer name property value"); 145 break; 146 case 'curv': // curves adjustment layer 147 adjustmentname=NSLocalizedString(@"Curves",@"Photoshop curves adjustment layer name property value"); 148 break; 149 case 'brit': // brightness adjustment layer 150 adjustmentname=NSLocalizedString(@"Brightness",@"Photoshop brightness adjustment layer name property value"); 151 break; 152 case 'blnc': // color balance adjustment layer 153 adjustmentname=NSLocalizedString(@"Color balance",@"Photoshop color balance adjustment layer name property value"); 154 break; 155 case 'hue ': // old hue/saturation adjustment layer 156 adjustmentname=NSLocalizedString(@"Hue/saturation (old)",@"Photoshop old hue/saturations adjustment layer name property value"); 157 break; 158 case 'hue2': // new hue/saturation adjustment layer 159 adjustmentname=NSLocalizedString(@"Hue/saturation",@"Photoshop hue/saturation adjustment layer name property value"); 160 break; 161 case 'selc': // selective color adjustment layer 162 adjustmentname=NSLocalizedString(@"Selective color",@"Photoshop selective color adjustment layer name property value"); 163 break; 164 case 'thrs': // threshold adjustment layer 165 adjustmentname=NSLocalizedString(@"Threshold",@"Photoshop threshold adjustment layer name property value"); 166 break; 167 case 'nvrt': // invert adjustment layer 168 adjustmentname=NSLocalizedString(@"Invert",@"Photoshop invert adjustment layer name property value"); 169 break; 170 case 'post': // posterize adjustment layer 171 adjustmentname=NSLocalizedString(@"Posterize",@"Photoshop posterize adjustment layer name property value"); 172 break; 173 174 case 'lrFX': // effects layer 175 break; 176 177 case 'tySh': // type tool 178 { 179 NSMutableArray *fonts=[NSMutableArray array]; 180 181 [fh skipBytes:52]; 182 int numfonts=[fh readUInt16BE]; 183 for(int i=0;i<numfonts;i++) 184 { 185 [fh skipBytes:6]; 186 int len=[fh readUInt8]; 187 NSString *fontname=[[[NSString alloc] initWithData:[fh readDataOfLength:len] encoding:NSISOLatin1StringEncoding] autorelease]; 188 len=[fh readUInt8]; 189 [fh skipBytes:len]; // family 190 len=[fh readUInt8]; 191 NSString *fontstyle=[[[NSString alloc] initWithData:[fh readDataOfLength:len] encoding:NSISOLatin1StringEncoding] autorelease]; 192 [fh skipBytes:2]; 193 int vecnum=[fh readUInt32BE]; 194 [fh skipBytes:vecnum*4]; 195 196 [fonts addObject:[NSString stringWithFormat:@"%@ %@",fontname,fontstyle]]; 197 } 198 199 typetoolfonts=[XeePropertyItem itemsWithLabel: 200 NSLocalizedString(@"Text layer fonts",@"Photoshop text layer fonts property title") 201 valueArray:fonts]; 202 203 int numstyles=[fh readUInt16BE]; 204 [fh skipBytes:numstyles*26+26]; 205 206 NSMutableString *str=[NSMutableString string]; 207 int numlines=[fh readUInt16BE]; 208 for(int i=0;i<numlines;i++) 209 { 210 int numchars=[fh readUInt32BE]; 211 [fh skipBytes:4]; 212 for(int j=0;j<numchars;j++) 213 { 214 int c=[fh readUInt16BE]; 215 [fh skipBytes:2]; 216 if(c=='\n') continue; // skip \n 217 if(c=='\r') c='\n'; // and turn \r into \n; 218 [str appendFormat:@"%C",c]; 219 } 220 } 221 222 typetooltext=[XeePropertyItem itemsWithLabel: 223 NSLocalizedString(@"Text layer contents",@"Photoshop text layer contents property title") 224 textValue:str]; 225 } 226 break; 227 228 case 'luni': // unicode layer name 229 { 230 if(chunklen<4) break; 231 int len=[fh readUInt32BE]; 232 if(chunklen<4+len*2) break; 233 layername=[[[NSString alloc] initWithData:[fh readDataOfLength:len*2] 234 encoding:CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF16BE)] 235 autorelease]; 236 } 237 break; 238 239 case 'lyid': // layer ID name 240 break; 241 case 'lfx2': // object based effects layer 242 break; 243 case 'Patt': // patterns 244 break; 245 case 'clbl': // blend clipping elements 246 break; 247 case 'infx': // blend interior elements 248 break; 249 case 'knko': // knockout setting 250 break; 251 case 'lspf': // protected setting 252 break; 253 case 'lclr': // sheet color setting 254 break; 255 case 'fxrp': // reference point 256 break; 257 case 'grdm': // gradient settings 258 break; 259 case 'lsct': // section divider setting 260 break; 261 case 'brst': // channel blending restrictions setting 262 break; 263 case 'SoCo': // solid color sheet setting 264 break; 265 case 'PtFl': // pattern fill setting 266 break; 267 case 'GdFl': // gradient fill setting 268 break; 269 case 'vmsk': // vector mask setting 270 break; 271 272 case 'TySh': // type tool object setting 273 { 274 [fh skipBytes:50]; 275 if([fh readUInt16BE]!=50) break; // text descriptor version 276 if([fh readUInt32BE]!=16) break; // descriptor version 277 int classlen=[fh readUInt32BE]; 278 [fh skipBytes:classlen*2]; 279 if([fh readUInt32BE]!=0) break; 280 if([fh readUInt32BE]!='TxLr') break; 281 282 int numitems=[fh readUInt32BE]; 283 284 if(numitems==0) break; 285 if([fh readUInt32BE]!=0) break; 286 if([fh readUInt32BE]!='Txt ') break; 287 if([fh readUInt32BE]!='TEXT') break; 288 289 int textlen=[fh readUInt32BE]; 290 291 NSMutableString *str=[[[NSMutableString alloc] initWithData:[fh readDataOfLength:textlen*2] 292 encoding:CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF16BE)] 293 autorelease]; 294 295 int len=[str length]; 296 for(int i=0;i<len;i++) 297 if([str characterAtIndex:i]=='\r') [str replaceCharactersInRange:NSMakeRange(i,1) withString:@"\n"]; 298 299 typetooltext=[XeePropertyItem itemsWithLabel: 300 NSLocalizedString(@"Text layer contents",@"Photoshop text layer contents property title") 301 textValue:str]; 302 303/* NSData *rest=[fh readDataOfLength:nextchunk-[fh offsetInFile]]; 304 const uint8_t *restbytes=[rest bytes]; 305 int restlen=[rest length]; 306 int offs=XeeOffsetOfStringInMemory("EngineDatatdta",restbytes,restlen); 307 if(offs>=0) 308 { 309 int blocklen=XeeBEUInt32(restbytes+offs+14); 310 if(offs+18+blocklen>restlen) break; 311// NSData *block=[rest subdataWithRange:NSMakeRange(offs+18,blocklen)]; 312 NSMutableData *block=[NSMutableData dataWithData:[rest subdataWithRange:NSMakeRange(offs+18,blocklen)]]; 313 uint8_t *bytes=[block mutableBytes]; 314 for(int i=0;i<[block length];i++) if(!isspace(bytes[i])&&bytes[i]<=0x20) bytes[i]='.'; 315 NSLog(@"%@",[[[NSString alloc] initWithData:block encoding:NSISOLatin1StringEncoding] autorelease]); 316 }*/ 317 } 318 break; 319 320 case 'ffxi': // foreign effect ID 321 break; 322 case 'lnsr': // layer name source setting 323 break; 324 case 'shpa': // pattern data 325 break; 326 case 'shmd': // meta data setting 327 NSLog(@"found layer metadata"); 328 break; 329 case 'Layr': // layer data - what 330 break; 331 } 332 [fh seekToFileOffset:nextchunk]; 333 } 334 335 outofbytes: 336 if(layername) [props addObject:[XeePropertyItem itemWithLabel: 337 NSLocalizedString(@"Layer name",@"Photoshop layer name property title") 338 value:layername]]; 339 340 [props addObject:[XeePropertyItem itemWithLabel: 341 NSLocalizedString(@"Opacity",@"Photoshop layer opacity property title") 342 value:[NSString stringWithFormat:@"%d%%",(opacity*100)/255]]]; 343 344 NSString *blendname; 345 switch(blendmode) 346 { 347 case 'norm': blendname=NSLocalizedString(@"Normal",@"Photoshop layer normal blending mode name property value"); break; 348 case 'dark': blendname=NSLocalizedString(@"Darken",@"Photoshop layer darken blending mode name property value"); break; 349 case 'lite': blendname=NSLocalizedString(@"Lighten",@"Photoshop layer lighten blending mode name property value"); break; 350 case 'hue ': blendname=NSLocalizedString(@"Hue",@"Photoshop layer hue blending mode name property value"); break; 351 case 'sat ': blendname=NSLocalizedString(@"Saturation",@"Photoshop layer normal blending mode name property value"); break; 352 case 'colr': blendname=NSLocalizedString(@"Color",@"Photoshop layer color blending mode name property value"); break; 353 case 'lum ': blendname=NSLocalizedString(@"Luminosity",@"Photoshop layer luminosity blending mode name property value"); break; 354 case 'mul ': blendname=NSLocalizedString(@"Multiply",@"Photoshop layer multiply blending mode name property value"); break; 355 case 'scrn': blendname=NSLocalizedString(@"Screen",@"Photoshop layer screen blending mode name property value"); break; 356 case 'diss': blendname=NSLocalizedString(@"Dissolve",@"Photoshop layer dissolve blending mode name property value"); break; 357 case 'over': blendname=NSLocalizedString(@"Overlay",@"Photoshop layer overlay blending mode name property value"); break; 358 case 'hLit': blendname=NSLocalizedString(@"Hard light",@"Photoshop layer hard light blending mode name property value"); break; 359 case 'sLit': blendname=NSLocalizedString(@"Soft light",@"Photoshop layer soft light blending mode name property value"); break; 360 case 'diff': blendname=NSLocalizedString(@"Difference",@"Photoshop layer difference blending mode name property value"); break; 361 case 'smud': blendname=NSLocalizedString(@"Exclusion",@"Photoshop layer exclusion blending mode name property value"); break; 362 case 'div ': blendname=NSLocalizedString(@"Color dodge",@"Photoshop layer color dodge blending mode name property value"); break; 363 case 'idiv': blendname=NSLocalizedString(@"Color burn",@"Photoshop layer color burn blending mode name property value"); break; 364 default: 365 blendname=[NSString stringWithFormat: 366 NSLocalizedString(@"Unknown (%c%c%c%c)",@"Photoshop layer unknown blending mode name property value"), 367 (blendmode>>24)&0xff,(blendmode>>16)&0xff,(blendmode>>8)&0xff,blendmode&0xff]; 368 break; 369 } 370 [props addObject:[XeePropertyItem itemWithLabel: 371 NSLocalizedString(@"Blending mode",@"Photoshop layer blending mode property title") 372 value:blendname]]; 373 374 if(adjustmentname) [props addObject:[XeePropertyItem itemWithLabel: 375 NSLocalizedString(@"Adjustment layer",@"Photoshop adjustment layer property title") 376 value:adjustmentname]]; 377 378 if(typetooltext) [props addObjectsFromArray:typetooltext]; 379 if(typetoolfonts) [props addObjectsFromArray:typetoolfonts]; 380 381 [fh seekToFileOffset:nextoffs]; 382 } 383 return self; 384} 385 386-(void)dealloc 387{ 388 [handle release]; 389 [props release]; 390 [channeloffs release]; 391 [super dealloc]; 392} 393 394 395 396-(void)setDataOffset:(off_t)offset 397{ 398 dataoffs=offset; 399 [handle seekToFileOffset:offset]; 400 compression=[handle readUInt16BE]; 401} 402 403-(off_t)totalSize { return totalsize; } 404 405 406 407-(XeeImage *)image 408{ 409 XeeImage *image=nil; 410 BOOL hasalpha=[self hasAlpha]; 411 412 switch(mode) 413 { 414 case XeePhotoshopBitmapMode: 415 image=[[[XeeBitmapRawImage alloc] initWithHandle:[self handleForChannel:0] width:width height:height] autorelease]; 416 [image setDepthBitmap]; 417 break; 418 419// case XeePhotoshopIndexedMode: [self setDepthIndexed:1<<bitdepth]; break; 420 421 case XeePhotoshopGreyscaleMode: 422 case XeePhotoshopDuotoneMode: 423 image=[[[XeeRawImage alloc] initWithHandle:[self handleForNumberOfChannels:1] 424 width:width height:height depth:depth colourSpace:XeeGreyRawColourSpace 425 flags:XeeBigEndianRawFlag|(hasalpha?XeeAlphaLastRawFlag:0)|(depth==32?XeeFloatingPointRawFlag:0)] 426 autorelease]; 427 428 if(mode==XeePhotoshopGreyscaleMode) [image setDepthGrey:depth alpha:hasalpha floating:depth==32?YES:NO]; 429 else [image setDepth:[NSString stringWithFormat:NSLocalizedString(@"%d bits duotone",@"Description for duotone (Photoshop) images"),depth] 430 iconName:@"depth_rgb"]; 431 break; 432 433 case XeePhotoshopRGBMode: 434 image=[[[XeeRawImage alloc] initWithHandle:[self handleForNumberOfChannels:3] 435 width:width height:height depth:depth colourSpace:XeeRGBRawColourSpace 436 flags:XeeBigEndianRawFlag|(hasalpha?XeeAlphaLastRawFlag:0)|(depth==32?XeeFloatingPointRawFlag:0)] 437 autorelease]; 438 439 [image setDepthRGB:depth alpha:hasalpha floating:depth==32?YES:NO]; 440 break; 441 442 case XeePhotoshopCMYKMode: 443 image=[[[XeeRawImage alloc] initWithHandle:[self handleForNumberOfChannels:4] 444 width:width height:height depth:depth colourSpace:XeeCMYKRawColourSpace 445 flags:XeeBigEndianRawFlag|(hasalpha?XeeAlphaLastRawFlag:0)] 446 autorelease]; 447 448 [(XeeRawImage *)image setZeroPoint:1 onePoint:0 forChannel:0]; 449 [(XeeRawImage *)image setZeroPoint:1 onePoint:0 forChannel:1]; 450 [(XeeRawImage *)image setZeroPoint:1 onePoint:0 forChannel:2]; 451 [(XeeRawImage *)image setZeroPoint:1 onePoint:0 forChannel:3]; 452 453 [image setDepthCMYK:depth alpha:hasalpha]; 454 break; 455 456 case XeePhotoshopLabMode: 457 image=[[[XeeRawImage alloc] initWithHandle:[self handleForNumberOfChannels:3] 458 width:width height:height depth:depth colourSpace:XeeLabRawColourSpace 459 flags:XeeBigEndianRawFlag|(hasalpha?XeeAlphaLastRawFlag:0)] 460 autorelease]; 461 462 [image setDepthLab:depth alpha:hasalpha]; 463 break; 464 465 default: 466 return nil; 467 } 468 469 [image setProperties:[NSArray arrayWithObject:[XeePropertyItem itemWithLabel: 470 NSLocalizedString(@"Photoshop layer properties",@"Photoshop layer properties section title") 471 value:props identifier:@"pslayer"]]]; 472 473 return image; 474} 475 476-(CSHandle *)handleForNumberOfChannels:(int)requiredchannels 477{ 478 NSMutableArray *array=[NSMutableArray array]; 479 for(int i=0;i<requiredchannels;i++) 480 { 481 CSHandle *fh=[self handleForChannel:i]; 482 if(!handle) return nil; 483 [array addObject:fh]; 484 } 485 CSHandle *alphahandle=[self handleForChannel:-1]; 486 if(alphahandle) [array addObject:alphahandle]; 487 488 if([array count]==1) return [array objectAtIndex:0]; 489 else return [[[XeeInterleavingHandle alloc] initWithHandles:array elementSize:depth] autorelease]; 490} 491 492-(CSHandle *)handleForChannel:(int)channel 493{ 494 NSNumber *offs=[channeloffs objectForKey:[NSNumber numberWithInt:channel]]; 495 if(!offs) return nil; 496 497 off_t start=dataoffs+2+[offs longLongValue]; 498 CSFileHandle *fh; 499 500 switch(compression) 501 { 502 case 0: 503 fh=[[handle copy] autorelease]; 504 [fh seekToFileOffset:start]; 505 return fh; 506 507 case 1: 508 fh=[[handle copy] autorelease]; 509 [fh seekToFileOffset:start]; 510 return [[[XeePackbitsHandle alloc] initWithHandle:fh rows:height bytesPerRow:(width*depth+7)/8 511 channel:0 of:1 previousSize:0] autorelease]; 512 513 case 2: 514 fh=[[handle copy] autorelease]; 515 [fh seekToFileOffset:start]; 516 return [CSZlibHandle zlibHandleWithHandle:fh]; 517 518 case 3: 519 fh=[[handle copy] autorelease]; 520 [fh seekToFileOffset:start]; 521 return [[[XeeDeltaHandle alloc] initWithHandle:[CSZlibHandle zlibHandleWithHandle:fh] 522 depth:depth columns:width] autorelease]; 523 524 default: 525 return nil; 526 } 527} 528 529-(BOOL)hasAlpha { return [channeloffs objectForKey:[NSNumber numberWithInt:-1]]?YES:NO; } 530 531@end