1 /**
2     This PSD file parser
3 
4     This is more or less based on psd_sdk
5 
6     Various features of PSD_SDK are left out for simplicity
7 */
8 module psd.parser;
9 import utils.io;
10 import utils;
11 import psd;
12 import std.exception;
13 import std.format;
14 import std..string;
15 import psd.rle;
16 
17 /**
18     Parses document
19 */
20 PSD parseDocument(string fileName) {
21     auto file = File(fileName, "rb");
22     return parseDocument(file);
23 }
24 
25 /**
26     Parses a Photoshop Document
27 */
28 PSD parseDocument(ref File file) {
29     PSD psd;
30     file.seek(0); // Seek back to start of file, just in case.
31 
32     // First, parse the header section.
33     parseHeader(file, psd);
34 
35     // Parse the various sections
36     parseColorModeSection(file, psd);
37     //parseImageResourceSection(file, psd);
38     psd.imageResourceSectionOffset = file.tell();
39     psd.imageResourceSectionLength = file.readValue!uint;
40     file.skip(psd.imageResourceSectionLength);
41     
42     parseLayerMaskInfoSection(file, psd);
43     parseImageDataSectionOffset(file, psd);
44 
45     return psd;
46 }
47 
48 
49 
50 package(psd):
51 
52 /**
53     Finds the index of the channel
54 */
55 uint findChannel(Layer* layer, ChannelType channelType)
56 {
57 	for (uint i = 0; i < layer.channels.length; ++i)
58 	{
59 		const ChannelInfo* channel = &layer.channels[i];
60 		if ((channel.dataLength > 0) && channel.type == channelType)
61 			return i;
62 	}
63 
64 	return ChannelType.INVALID;
65 }
66 
67 
68 void extractLayer(ref Layer layer) {
69     auto file = layer.filePtr;
70 
71     // Skip empty layers
72     if (layer.width == 0 && layer.height == 0) return;
73 
74     const size_t channelCount = layer.channels.length;
75     //layer.data = new ubyte[layer.width*layer.height*channelCount];
76     
77     foreach(i; 0..channelCount) {
78         ChannelInfo* channel = &layer.channels[i];
79         file.seek(channel.fileOffset);
80 
81         // HACK: To allow transparency to be put as RGBA
82         //       an offset is applied based on its layer type.
83         size_t offset = i;
84         if (channelCount > 3) {
85             switch(channel.type) {
86                 case -1:
87                     offset = 3;
88                     break;
89                 default:
90                     offset = channel.type;
91                     break;
92             }
93         }
94 
95         const ushort compressionType = file.readValue!ushort;
96         switch(compressionType) {
97             //RAW
98             case 0:
99                 channel.data = new ubyte[layer.width*layer.height];
100                 file.rawRead(channel.data);
101                 break;
102             
103             // RLE
104             case 1:
105 
106                 // RLE compressed data is preceded by a 2-byte data count for each scanline
107                 uint rleDataSize;
108                 foreach(_; 0..layer.height) {
109                     const ushort dataCount = file.readValue!ushort;
110                     rleDataSize += dataCount;
111                 }
112 
113                 if (rleDataSize > 0) {
114 
115                     // Read planar data
116                     ubyte[] rleData = new ubyte[rleDataSize];
117 
118                     // We need to work around the same D bug as before.
119                     file.rawRead(rleData);
120 
121                     // Decompress RLE
122                     // FIXME:  We're assuming psd.channelsPerBit == 8 right now, and that's not 
123                     //         always the case.
124                     channel.data = new ubyte[layer.width*layer.height];
125                     decodeRLE(rleData, channel.data);
126                 }
127                 break;
128             default: assert(0, "Unsupported compression type.");
129         }
130 
131     }
132 
133     // Transcode to RGBA
134     auto rgba = new ubyte[layer.width*layer.height*channelCount];
135     auto rgbaPtr = rgba.ptr;
136     
137 
138     // Channel indices.
139     auto aI = findChannel(&layer, ChannelType.TRANSPARENCY_MASK);
140     auto rI = findChannel(&layer, ChannelType.R);
141     auto gI = findChannel(&layer, ChannelType.G);
142     auto bI = findChannel(&layer, ChannelType.B);
143     
144     
145     auto a = layer.channels[aI].data.ptr;
146     auto r = layer.channels[rI].data.ptr;
147     auto g = layer.channels[gI].data.ptr;
148     auto b = layer.channels[bI].data.ptr;
149 
150     for (size_t j = 0; j < rgba.length; j += 4)
151     {
152         rgba[j+0] = *r++;
153         rgba[j+1] = *g++;
154         rgba[j+2] = *b++;
155         rgba[j+3] = *a++;
156     }
157 
158     // 
159     //foreach (size_t y; 0 .. layer.height)
160     //{
161     //    ubyte* a = layer.data.ptr + (y * layer.width * channelCount) + (layer.width * aI);
162     //    ubyte* r = layer.data.ptr + (y * layer.width * channelCount) + (layer.width * rI);
163     //    ubyte* g = layer.data.ptr + (y * layer.width * channelCount) + (layer.width * gI);
164     //    ubyte* b = layer.data.ptr + (y * layer.width * channelCount) + (layer.width * bI);
165     //    foreach (size_t x; 0 .. layer.width)
166     //    {
167     //        *rgbaPtr++ = *r++;
168     //        *rgbaPtr++ = *g++;
169     //        *rgbaPtr++ = *b++;
170     //        *rgbaPtr++ = *a++;   
171     //    }
172     //}
173 
174     layer.data = rgba;
175 }
176 
177 private:
178 
179 /*
180                                 PSD HEADER
181 */
182 void parseHeader(ref File file, ref PSD psd) {
183     
184     // Check signature
185     {
186         enforce(file.readStr(4) == "8BPS", "Invalid file, must be a Photoshop PSD file. PSB's are not supported!");
187     }
188 
189     // Check version (must be 1)
190     {
191         enforce(file.readValue!ushort() == 1, "Version does not match 1.");
192     }
193 
194     // Check reserve bytes
195     {
196         enforce(file.read(6) == [0, 0, 0, 0, 0, 0], "Unexpected reserve bytes, file may be corrupted.");
197     }
198 
199     // Read number of channels
200     // This is the number of channels contained in the document for all layers, including alpha channels.
201     // e.g. for an RGB document with 3 alpha channels, this would be 3 (RGB) + 3 (Alpha) = 6 channels
202     // however, note that the individual layers can have extra channels for transparency masks, vector masks, and user masks.
203     // this is different from layer to layer.
204     psd.channels = file.readValue!ushort;
205 
206     // Read rest of header info
207     psd.height = file.readValue!uint;
208     psd.width = file.readValue!uint;
209     psd.bitsPerChannel = file.readValue!ushort;
210     psd.colorMode = cast(ColorMode)file.readValue!ushort;
211 }
212 
213 
214 
215 
216 /*
217                                 COLOR MODE DATA
218 */
219 void parseColorModeSection(ref File file, ref PSD psd) {
220     psd.colorModeDataSectionLength = file.readValue!uint;
221     psd.colorModeDataSectionOffset = file.tell();
222 
223     file.skip(psd.colorModeDataSectionLength);
224 }
225 
226 
227 
228 
229 /*
230                                 IMAGE RESOURCES
231 */
232 
233 /**
234     Reads a padded value.
235 */
236 pragma(inline, true)
237 T readPaddedValue(T)(ref File file, T multipleOf = 2, T addTo = 0) {
238     T value = file.readValue!T;
239     return cast(T)roundUpToMultiple(value + addTo, multipleOf);
240 }
241 
242 void parseImageResourceSection(ref File file, ref PSD psd) {
243     psd.imageResourceSectionLength = file.readValue!uint;
244     psd.imageResourceSectionOffset = file.tell();
245 
246     // TODO: read
247     ulong leftToRead = psd.imageResourceSectionLength;
248 
249     while (leftToRead > 0) {
250         string signature = file.readStr(4);
251         
252         enforce(signature == "8BIM" || signature == "psdM", 
253             "Image resources section seems to be corrupt, signature does not match \"8BIM\" nor \"psdM\".");
254 
255         const ushort id = file.readValue!ushort;
256 
257         const ubyte nameLength = readPaddedValue!ubyte(file, 2, 1);
258         const string name = file.readStr(nameLength - 1);
259         
260         const uint resourceSize = readPaddedValue!uint(file);
261 
262         switch (id) {
263 			case ImageResourceType.IPTC_NAA:
264 			case ImageResourceType.CAPTION_DIGEST:
265 			case ImageResourceType.PRINT_INFORMATION:
266 			case ImageResourceType.PRINT_STYLE:
267 			case ImageResourceType.PRINT_SCALE:
268 			case ImageResourceType.PRINT_FLAGS:
269 			case ImageResourceType.PRINT_FLAGS_INFO:
270 			case ImageResourceType.PRINT_INFO:
271 			case ImageResourceType.RESOLUTION_INFO:
272 			case ImageResourceType.GLOBAL_ANGLE:
273 			case ImageResourceType.GLOBAL_ALTITUDE:
274 			case ImageResourceType.COLOR_HALFTONING_INFO:
275 			case ImageResourceType.COLOR_TRANSFER_FUNCTIONS:
276 			case ImageResourceType.MULTICHANNEL_HALFTONING_INFO:
277 			case ImageResourceType.MULTICHANNEL_TRANSFER_FUNCTIONS:
278 			case ImageResourceType.LAYER_STATE_INFORMATION:
279 			case ImageResourceType.LAYER_GROUP_INFORMATION:
280 			case ImageResourceType.LAYER_GROUP_ENABLED_ID:
281 			case ImageResourceType.LAYER_SELECTION_ID:
282 			case ImageResourceType.GRID_GUIDES_INFO:
283 			case ImageResourceType.URL_LIST:
284 			case ImageResourceType.SLICES:
285 			case ImageResourceType.PIXEL_ASPECT_RATIO:
286 			case ImageResourceType.ICC_UNTAGGED_PROFILE:
287 			case ImageResourceType.ID_SEED_NUMBER:
288 			case ImageResourceType.BACKGROUND_COLOR:
289 			case ImageResourceType.ALPHA_CHANNEL_UNICODE_NAMES:
290 			case ImageResourceType.ALPHA_IDENTIFIERS:
291 			case ImageResourceType.COPYRIGHT_FLAG:
292 			case ImageResourceType.PATH_SELECTION_STATE:
293 			case ImageResourceType.ONION_SKINS:
294 			case ImageResourceType.TIMELINE_INFO:
295 			case ImageResourceType.SHEET_DISCLOSURE:
296 			case ImageResourceType.WORKING_PATH:
297 			case ImageResourceType.MAC_PRINT_MANAGER_INFO:
298 			case ImageResourceType.WINDOWS_DEVMODE:
299 				// we are currently not interested in this resource type, skip it
300 			default:
301 				// this is a resource we know nothing about, so skip it
302 				file.skip(resourceSize);
303 				break;
304 			
305 			case ImageResourceType.DISPLAY_INFO:
306 			{
307 				// the display info resource stores color information and opacity for extra channels contained
308 				// in the document. these extra channels could be alpha/transparency, as well as spot color
309 				// channels used for printing.
310 			
311 				// check whether storage for alpha channels has been allocated yet
312 				// (ImageResourceType.ALPHA_CHANNEL_ASCII_NAMES stores the channel names)
313 				if (psd.imageResourcesData.alphaChannels.length == 0)
314 				{
315 					// note that this assumes RGB mode
316 					const uint channelCount = psd.channels - 3;
317 					psd.imageResourcesData.alphaChannelCount = channelCount;
318 					psd.imageResourcesData.alphaChannels.length = channelCount;
319 				}
320 			
321 				const uint versionNum = file.readValue!uint;
322 			
323 				for (uint i = 0u; i < psd.imageResourcesData.alphaChannelCount; ++i) {
324                     AlphaChannel* channel = &psd.imageResourcesData.alphaChannels[i];
325 					channel.colorSpace = file.readValue!ushort;
326 					channel.color[0] = file.readValue!ushort;
327 					channel.color[1] = file.readValue!ushort;
328 					channel.color[2] = file.readValue!ushort;
329 					channel.color[3] = file.readValue!ushort;
330 					channel.opacity = file.readValue!ushort;
331 					channel.mode = cast(AlphaChannel.Mode)file.readValue!ubyte;
332 				}
333 			}
334 			break;
335 			
336 			case ImageResourceType.VERSION_INFO:
337 			{
338 				const uint versionNum = file.readValue!uint;
339 				const ubyte hasRealMergedData = file.readValue!ubyte;
340 				psd.imageResourcesData.containsRealMergedData = (hasRealMergedData != 0u);
341 				file.skip(resourceSize - 5u);
342 			}
343 			break;
344 			
345 			case ImageResourceType.THUMBNAIL_RESOURCE:
346 			{
347 				const uint format = file.readValue!uint;
348 				const uint width = file.readValue!uint;
349 				const uint height = file.readValue!uint;
350 				const uint widthInBytes = file.readValue!uint;
351 				const uint totalSize = file.readValue!uint;
352 				const uint binaryJpegSize = file.readValue!uint;
353 			
354 				const ushort bitsPerPixel = file.readValue!ushort;
355 				const ushort numberOfPlanes = file.readValue!ushort;
356 			
357 				psd.imageResourcesData.thumbnail.width = width;
358 				psd.imageResourcesData.thumbnail.height = height;
359 				psd.imageResourcesData.thumbnail.binaryJpegSize = binaryJpegSize;
360 				psd.imageResourcesData.thumbnail.binaryJpeg = file.rawRead(new ubyte[binaryJpegSize]);
361 				
362 				const uint bytesToSkip = resourceSize - 28u - binaryJpegSize;
363 				file.skip(bytesToSkip);
364 			}
365 			break;
366 			
367 			case ImageResourceType.XMP_METADATA:
368 			{
369 				// load the XMP metadata as raw data
370                 enforce(psd.imageResourcesData.xmpMetadata.length != 0, "File contains more than one XMP metadata resource.");
371 				psd.imageResourcesData.xmpMetadata = file.rawRead(new ubyte[resourceSize]);
372 			}
373 			break;
374 			
375 			case ImageResourceType.ICC_PROFILE:
376 			{
377 				// load the ICC profile as raw data
378                 enforce(psd.imageResourcesData.iccProfile.length != 0, "File contains more than one ICC profile.");
379 				psd.imageResourcesData.iccProfile = file.rawRead(new ubyte[resourceSize]);
380 				psd.imageResourcesData.sizeOfICCProfile = resourceSize;
381 			}
382 			break;
383 			
384 			case ImageResourceType.EXIF_DATA:
385 			{
386 				// load the EXIF data as raw data
387                 enforce(psd.imageResourcesData.exifData.length != 0, "File contains more than one EXIF data block.");
388 				psd.imageResourcesData.exifData = file.rawRead(new ubyte[resourceSize]);
389 				psd.imageResourcesData.sizeOfExifData = resourceSize;
390 			}
391 			break;
392 			
393 			case ImageResourceType.ALPHA_CHANNEL_ASCII_NAMES:
394 			{
395 				// check whether storage for alpha channels has been allocated yet
396 				// (ImageResourceType.DISPLAY_INFO stores the channel color data)
397 				if (psd.imageResourcesData.alphaChannels.length == 0)
398 				{
399 					// note that this assumes RGB mode
400 					const uint channelCount = psd.channels - 3;
401 					psd.imageResourcesData.alphaChannelCount = channelCount;
402 					psd.imageResourcesData.alphaChannels.length = channelCount;
403 				}
404 			
405 				// the names of the alpha channels are stored as a series of Pascal strings
406 				uint channel = 0;
407 				long remaining = resourceSize;
408 				while (remaining > 0) {
409                     string channelName;
410 					const ubyte channelNameLength = file.readValue!ubyte;
411 					if (channelNameLength > 0) {
412                         channelName = file.readStr(channelNameLength);
413 					}
414 			
415 					remaining -= 1 + channelNameLength;
416 			
417 					if (channel < psd.imageResourcesData.alphaChannelCount) {
418 						psd.imageResourcesData.alphaChannels[channel].asciiName = channelName;
419 						++channel;
420 					}
421 				}
422 			}
423 			break;
424         }
425 
426 		leftToRead -= 10 + nameLength + resourceSize;
427     }
428 }
429 
430 
431 
432 
433 
434 
435 /*
436                                 LAYER MASK INFO
437 */
438 
439 // ---------------------------------------------------------------------------------------------------------------------
440 // ---------------------------------------------------------------------------------------------------------------------
441 long ReadMaskRectangle(ref File file, ref MaskData maskData)
442 {
443     maskData.top = file.readValue!int;
444     maskData.left = file.readValue!int;
445     maskData.bottom = file.readValue!int;
446     maskData.right = file.readValue!int;
447 
448     return 4u*int.sizeof;
449 }
450 
451 
452 // ---------------------------------------------------------------------------------------------------------------------
453 // ---------------------------------------------------------------------------------------------------------------------
454 long ReadMaskDensity(ref File file, ref ubyte density)
455 {
456     density = file.readValue!ubyte;
457     return ubyte.sizeof;
458 }
459 
460 
461 // ---------------------------------------------------------------------------------------------------------------------
462 // ---------------------------------------------------------------------------------------------------------------------
463 long ReadMaskFeather(ref File file, ref double feather)
464 {
465     feather = file.readValue!double;
466     return double.sizeof;
467 }
468 
469 
470 // ---------------------------------------------------------------------------------------------------------------------
471 // ---------------------------------------------------------------------------------------------------------------------
472 long ReadMaskParameters(ref File file, ref ubyte layerDensity, ref double layerFeather, ref ubyte vectorDensity, ref double vectorFeather)
473 {
474     long bytesRead = 0;
475 
476     const ubyte flags = file.readValue!ubyte;
477     bytesRead += ubyte.sizeof;
478 
479     const bool hasUserDensity = (flags & (1u << 0)) != 0;
480     const bool hasUserFeather = (flags & (1u << 1)) != 0;
481     const bool hasVectorDensity = (flags & (1u << 2)) != 0;
482     const bool hasVectorFeather = (flags & (1u << 3)) != 0;
483     if (hasUserDensity)
484     {
485         bytesRead += ReadMaskDensity(file, layerDensity);
486     }
487     if (hasUserFeather)
488     {
489         bytesRead += ReadMaskFeather(file, layerFeather);
490     }
491     if (hasVectorDensity)
492     {
493         bytesRead += ReadMaskDensity(file, vectorDensity);
494     }
495     if (hasVectorFeather)
496     {
497         bytesRead += ReadMaskFeather(file, vectorFeather);
498     }
499 
500     return bytesRead;
501 }
502 
503 // ---------------------------------------------------------------------------------------------------------------------
504 // ---------------------------------------------------------------------------------------------------------------------
505 template ApplyMaskData(T)
506 {
507     void ApplyMaskData(ref const MaskData maskData, double feather, ubyte density, T* layerMask)
508     {
509         layerMask.top = maskData.top;
510         layerMask.left = maskData.left;
511         layerMask.bottom = maskData.bottom;
512         layerMask.right = maskData.right;
513         layerMask.feather = feather;
514         layerMask.density = density;
515         layerMask.defaultColor = maskData.defaultColor;
516     }
517 }
518 
519 
520 
521 
522 
523 
524 
525 
526 
527 
528 
529 
530 
531 
532 void parseLayerMaskInfoSection(ref File file, ref PSD psd) {
533     psd.layerMaskInfoSectionLength = file.readValue!uint;
534     psd.layerMaskInfoSectionOffset = file.tell();
535     
536     // Parse the length of the layer info section
537     uint layerInfoSectionLength = file.readValue!uint;
538     LayerMaskSection* layerMaskSection = parseLayer(file, psd, psd.layerMaskInfoSectionOffset, cast(uint)psd.layerMaskInfoSectionLength, cast(uint)layerInfoSectionLength);
539 
540     // TODO: Build hirearchy
541     psd.layers = layerMaskSection.layers;
542 }
543 
544 LayerMaskSection* parseLayer(ref File file, ref PSD psd, ulong sectionOffset, uint sectionLength, uint layerLength) {
545     LayerMaskSection* layerMaskSection = new LayerMaskSection;
546 
547     if (layerLength != 0) {
548 
549         // Read the layer count. If it is a negative number, its absolute value is the number of the layers and the
550         // first alpha channel contains the transparency data for the merged result.
551         // this will also be reflected in the channelCount of the document.
552         short layerCount = file.readValue!short;
553         layerMaskSection.hasTransparencyMask = (layerCount < 0);
554         if (layerCount < 0) layerCount *= -1;
555 
556         layerMaskSection.layerCount = cast(uint)layerCount;
557         layerMaskSection.layers = new Layer[layerCount];
558 
559         foreach(i; 0..layerMaskSection.layers.length) {
560             Layer layer;
561             layer.filePtr = file;
562             layer.type = LayerType.Any;
563 
564             layer.top = file.readValue!int;
565             layer.left = file.readValue!int;
566 
567             // NOTE: It breaks here WTF???
568             layer.bottom = file.readValue!int;
569             layer.right = file.readValue!int;
570 
571             // Number of channels in the layer.
572             // this includes channels for transparency, layer, and vector masks, if any.
573             const ushort channelCount = file.readValue!ushort;
574             layer.channels = new ChannelInfo[channelCount];
575 
576             foreach(j; 0..layer.channels.length) {
577                 ChannelInfo* channel = &layer.channels[j];
578                 channel.type = file.readValue!short;
579                 channel.dataLength = file.readValue!uint;
580             }
581 
582             auto blendModeSignature = file.readStr(4);
583             enforce(blendModeSignature == "8BIM", "Layer mask info section seems to be corrupt, signature does not match \"8BIM\". (was \"%s\")".format(blendModeSignature));
584 
585             //layer.blendModeKey = cast(BlendingMode)file.readStr(4);
586             layer.blendModeKey = file.readValue!uint;
587             layer.opacity = file.readValue!ubyte;
588             layer.clipping = !file.readValue!bool;
589             layer.flags = cast(LayerFlags)file.readValue!ubyte;
590 
591             file.skip(1);
592 
593             const uint extraDataLength = file.readValue!uint;
594             const uint layerMaskDataLength = file.readValue!uint;
595             
596             // the layer mask data section is weird. it may contain extra data for masks, such as density and feather parameters.
597             // there are 3 main possibilities:
598             //	*) length == zero		.	skip this section
599             //	*) length == [20, 28]	.	there is one mask, and that could be either a layer or vector mask.
600             //								the mask flags give rise to mask parameters. they store the mask type, and additional parameters, if any.
601             //								there might be some padding at the end of this section, and its size depends on which parameters are there.
602             //	*) length == [36, 56]	.	there are two masks. the first mask has parameters, but does NOT store flags yet.
603             //								instead, there comes a second section with the same info (flags, default color, rectangle), and
604             //								the parameters follow after that. there is also padding at the end of this second section.
605             if (layerMaskDataLength != 0)
606             {
607                 // there can be at most two masks, one layer and one vector mask
608                 MaskData[2] maskData;
609                 uint maskCount = 1u;
610 
611                 double layerFeather = 0.0;
612                 double vectorFeather = 0.0;
613                 ubyte layerDensity = 0;
614                 ubyte vectorDensity = 0;
615 
616                 long toRead = layerMaskDataLength;
617 
618                 // enclosing rectangle
619                 toRead -= ReadMaskRectangle(file, maskData[0]);
620 
621                 maskData[0].defaultColor = file.readValue!ubyte;
622                 toRead -= ubyte.sizeof;
623 
624                 const ubyte maskFlags = file.readValue!ubyte;
625                 toRead -= ubyte.sizeof;
626 
627                 maskData[0].isVectorMask = (maskFlags & (1u << 3)) != 0;
628                 bool maskHasParameters = (maskFlags & (1u << 4)) != 0;
629                 if (maskHasParameters && (layerMaskDataLength <= 28))
630                 {
631                     toRead -= ReadMaskParameters(file, layerDensity, layerFeather, vectorDensity, vectorFeather);
632                 }
633 
634                 // check if there is enough data left for another section of mask data
635                 if (toRead >= 18)
636                 {
637                     // in case there is still data left to read, the following values are for the real layer mask.
638                     // the data we just read was for the vector mask.
639                     maskCount = 2u;
640 
641                     const ubyte realFlags = file.readValue!ubyte;
642                     toRead -= ubyte.sizeof;
643 
644                     maskData[1].defaultColor = file.readValue!ubyte;
645                     toRead -= ubyte.sizeof;
646 
647                     toRead -= ReadMaskRectangle(file, maskData[1]);
648 
649                     maskData[1].isVectorMask = (realFlags & (1u << 3)) != 0;
650 
651                     // note the OR here. whether the following section has mask parameter data or not is influenced by
652                     // the availability of parameter data of the previous mask!
653                     maskHasParameters |= ((realFlags & (1u << 4)) != 0);
654                     if (maskHasParameters)
655                     {
656                         toRead -= ReadMaskParameters(file, layerDensity, layerFeather, vectorDensity, vectorFeather);
657                     }
658                 }
659 
660                 // skip the remaining padding bytes, if any
661                 enforce(toRead >= 0, format("Parsing failed, #d bytes left", toRead));
662                 file.skip(cast(ulong)toRead);
663 
664                 // apply mask data to our own data structures
665                 for (uint mask=0; mask < maskCount; ++mask)
666                 {
667                     const bool isVectorMask = maskData[mask].isVectorMask;
668                     if (isVectorMask)
669                     {
670                         enforce(layer.vectorMask == null, "A vector mask already exists.");
671                         layer.vectorMask = new VectorMask[1];
672                         layer.vectorMask[0].data = null;
673                         layer.vectorMask[0].fileOffset = 0;
674                         ApplyMaskData!VectorMask(maskData[mask], vectorFeather, vectorDensity, &layer.vectorMask[0]);
675                     }
676                     else
677                     {
678                         enforce(layer.layerMask == null, "A layer mask already exists.");
679                         layer.layerMask = new LayerMask[1];
680                         layer.layerMask[0].data = null;
681                         layer.layerMask[0].fileOffset = 0;
682                         ApplyMaskData!LayerMask(maskData[mask], layerFeather, layerDensity, &layer.layerMask[0]);
683                     }
684                 }
685             }
686             
687             // skip blending ranges data, we are not interested in that for now
688             const uint layerBlendingRangesDataLength = file.readValue!uint;
689             file.skip(layerBlendingRangesDataLength);
690 
691             // the layer name is stored as pascal string, padded to a multiple of 4
692             // we peek here as the actual calculation happens inside readPascalStr
693             // TODO: make this more pretty?
694             const ubyte nameLength = file.peekValue!ubyte;
695             const uint paddedNameLength = roundUpToMultiple(nameLength + 1u, 4u);
696 
697             layer.name = file.readPascalStr(paddedNameLength - 1u);
698 
699             // read Additional Layer Information that exists since Photoshop 4.0.
700             // getting the size of this data is a bit awkward, because it's not stored explicitly somewhere. furthermore,
701             // the PSD format sometimes includes the 4-byte length in its section size, and sometimes not.
702             const uint additionalLayerInfoSize = extraDataLength - layerMaskDataLength - layerBlendingRangesDataLength - paddedNameLength - 8u;
703             long toRead = additionalLayerInfoSize;
704 
705             while (toRead > 0)
706             {
707                 const string signature = file.readStr(4);
708                 enforce(signature == "8BIM", "Additional Layer Information section seems to be corrupt, signature does not match \"8BIM\". (was \"%s\")".format(signature));
709 
710                 const string key = file.readStr(4);
711 
712                 // length needs to be rounded to an even number
713                 uint length = file.readValue!uint;
714                 length = roundUpToMultiple(length, 2u);
715 
716                 // read "Section divider setting" to identify whether a layer is a group, or a section divider
717                 if (key == "lsct")
718                 {
719                     layer.type = cast(LayerType)file.readValue!uint;
720 
721                     // skip the rest of the data
722                     file.skip(length - 4u);
723                 }
724                 // read Unicode layer name
725                 else if (key == "luni")
726                 {
727                     // PSD Unicode strings store 4 bytes for the number of characters, NOT bytes, followed by
728                     // 2-byte UTF16 Unicode data without the terminating null.
729                     const uint characterCountWithoutNull = file.readValue!uint;
730                     wstring utf16Name;
731                     for (uint c = 0u; c < characterCountWithoutNull; ++c)
732                     {
733                         utf16Name ~= cast(wchar)file.readValue!ushort;
734                     }
735 
736                     // If there's a unicode name we may as well use that here.
737                     import std.utf : toUTF8;
738                     layer.name = utf16Name.toUTF8;
739 
740                     // skip possible padding bytes
741                     file.skip(length - 4u - characterCountWithoutNull * ushort.sizeof);
742                 }
743                 else
744                 {
745                     file.skip(length);
746                 }
747 
748                 toRead -= 3*uint.sizeof + length;
749             }
750 
751             layerMaskSection.layers[i] = layer;
752         }
753 
754 
755         // walk through the layers and channels, but don't extract their data just yet. only save the file offset for extracting the
756         // data later.
757         foreach (i; 0..layerMaskSection.layers.length)
758         {            
759             Layer* layer = &layerMaskSection.layers[i];
760             foreach(j; 0..layer.channels.length) {
761                 ChannelInfo* channel = &layer.channels[j];
762                 channel.fileOffset = cast(uint)file.tell();
763                 file.skip(channel.dataLength);
764             }
765         }
766     }
767 
768     if (sectionLength > 0)
769     {
770         // start loading at the global layer mask info section, located after the Layer Information Section.
771         // note that the 4 bytes that stored the length of the section are not included in the length itself.
772         const ulong globalInfoSectionOffset = sectionOffset + layerLength + 4u;
773         file.seek(globalInfoSectionOffset);
774 
775         // work out how many bytes are left to read at this point. we need that to figure out the size of the last
776         // optional section, the Additional Layer Information.
777         if (sectionOffset + sectionLength > globalInfoSectionOffset)
778         {
779             long toRead = cast(long)(sectionOffset + sectionLength - globalInfoSectionOffset);
780             const uint globalLayerMaskLength = file.readValue!uint;
781             toRead -= uint.sizeof;
782 
783             if (globalLayerMaskLength != 0)
784             {
785                 layerMaskSection.overlayColorSpace = file.readValue!ushort;
786 
787                 // 4*2 byte color components
788                 file.skip(8);
789 
790                 layerMaskSection.opacity = file.readValue!ushort;
791                 layerMaskSection.kind = file.readValue!ubyte;
792 
793                 toRead -= 2u*ushort.sizeof + ubyte.sizeof + 8u;
794 
795                 // filler bytes (zeroes)
796                 const uint remaining = cast(uint)(globalLayerMaskLength - 2u*ushort.sizeof - ubyte.sizeof - 8u);
797                 file.skip(remaining);
798 
799                 toRead -= remaining;
800             }
801 
802             // are there still bytes left to read? then this is the Additional Layer Information that exists since Photoshop 4.0.
803             while (toRead > 0)
804             {
805                 const string signature = file.readStr(4);
806                 enforce(signature == "8BIM", "Additional Layer Information section seems to be corrupt, signature does not match \"8BIM\".");
807 
808                 const string key = file.readStr(4);
809 
810                 // again, length is rounded to a multiple of 4
811                 uint length = file.readValue!uint;
812                 length = roundUpToMultiple(length, 4u);
813 
814                 if (key == "Lr16")
815                 {
816                     const ulong offset = file.tell();
817                     
818                     // NOTE: I think in D we...can just let this get copied over?
819                     //DestroyLayerMaskSection(layerMaskSection, allocator);
820                     layerMaskSection = parseLayer(file, psd, 0u, 0u, length);
821                     file.seek(offset + length);
822                 }
823                 else if (key == "Lr32")
824                 {
825                     const ulong offset = file.tell();
826                     
827                     // NOTE: I think in D we...can just let this get copied over?
828                     //DestroyLayerMaskSection(layerMaskSection, allocator);
829 
830                     layerMaskSection = parseLayer(file, psd, 0u, 0u, length);
831                     file.seek(offset + length);
832                 }
833                 else if (key == "vmsk")
834                 {
835                     // TODO: could read extra vector mask data here
836                     file.skip(length);
837                 }
838                 else if (key == "lnk2")
839                 {
840                     // TODO: could read individual smart object layer data here
841                     file.skip(length);
842                 }
843                 else
844                 {
845                     file.skip(length);
846                 }
847 
848                 toRead -= 3u*uint.sizeof + length;
849             }
850         }
851     }
852 
853     return layerMaskSection;
854 }
855 
856 
857 /*
858                                 IMAGE DATA
859 */
860 void parseImageDataSectionOffset(ref File file, ref PSD psd) {
861     psd.imageDataSectionOffset = file.tell();
862     psd.imageDataSectionLength = file.size() - psd.imageDataSectionOffset;
863 
864     // TODO: read
865 }