PageRenderTime 96ms CodeModel.GetById 15ms app.highlight 77ms RepoModel.GetById 1ms app.codeStats 0ms

/XeePhotoshopLayerParser.m

https://code.google.com/p/xee/
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