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.opacity = file.readValue!ubyte;
587             layer.clipping = !file.readValue!bool;
588             layer.flags = cast(LayerFlags)file.readValue!ubyte;
589 
590             file.skip(1);
591 
592             const uint extraDataLength = file.readValue!uint;
593             const uint layerMaskDataLength = file.readValue!uint;
594             
595             // the layer mask data section is weird. it may contain extra data for masks, such as density and feather parameters.
596             // there are 3 main possibilities:
597             //	*) length == zero		.	skip this section
598             //	*) length == [20, 28]	.	there is one mask, and that could be either a layer or vector mask.
599             //								the mask flags give rise to mask parameters. they store the mask type, and additional parameters, if any.
600             //								there might be some padding at the end of this section, and its size depends on which parameters are there.
601             //	*) length == [36, 56]	.	there are two masks. the first mask has parameters, but does NOT store flags yet.
602             //								instead, there comes a second section with the same info (flags, default color, rectangle), and
603             //								the parameters follow after that. there is also padding at the end of this second section.
604             if (layerMaskDataLength != 0)
605             {
606                 // there can be at most two masks, one layer and one vector mask
607                 MaskData[2] maskData;
608                 uint maskCount = 1u;
609 
610                 double layerFeather = 0.0;
611                 double vectorFeather = 0.0;
612                 ubyte layerDensity = 0;
613                 ubyte vectorDensity = 0;
614 
615                 long toRead = layerMaskDataLength;
616 
617                 // enclosing rectangle
618                 toRead -= ReadMaskRectangle(file, maskData[0]);
619 
620                 maskData[0].defaultColor = file.readValue!ubyte;
621                 toRead -= ubyte.sizeof;
622 
623                 const ubyte maskFlags = file.readValue!ubyte;
624                 toRead -= ubyte.sizeof;
625 
626                 maskData[0].isVectorMask = (maskFlags & (1u << 3)) != 0;
627                 bool maskHasParameters = (maskFlags & (1u << 4)) != 0;
628                 if (maskHasParameters && (layerMaskDataLength <= 28))
629                 {
630                     toRead -= ReadMaskParameters(file, layerDensity, layerFeather, vectorDensity, vectorFeather);
631                 }
632 
633                 // check if there is enough data left for another section of mask data
634                 if (toRead >= 18)
635                 {
636                     // in case there is still data left to read, the following values are for the real layer mask.
637                     // the data we just read was for the vector mask.
638                     maskCount = 2u;
639 
640                     const ubyte realFlags = file.readValue!ubyte;
641                     toRead -= ubyte.sizeof;
642 
643                     maskData[1].defaultColor = file.readValue!ubyte;
644                     toRead -= ubyte.sizeof;
645 
646                     toRead -= ReadMaskRectangle(file, maskData[1]);
647 
648                     maskData[1].isVectorMask = (realFlags & (1u << 3)) != 0;
649 
650                     // note the OR here. whether the following section has mask parameter data or not is influenced by
651                     // the availability of parameter data of the previous mask!
652                     maskHasParameters |= ((realFlags & (1u << 4)) != 0);
653                     if (maskHasParameters)
654                     {
655                         toRead -= ReadMaskParameters(file, layerDensity, layerFeather, vectorDensity, vectorFeather);
656                     }
657                 }
658 
659                 // skip the remaining padding bytes, if any
660                 enforce(toRead >= 0, format("Parsing failed, #d bytes left", toRead));
661                 file.skip(cast(ulong)toRead);
662 
663                 // apply mask data to our own data structures
664                 for (uint mask=0; mask < maskCount; ++mask)
665                 {
666                     const bool isVectorMask = maskData[mask].isVectorMask;
667                     if (isVectorMask)
668                     {
669                         enforce(layer.vectorMask == null, "A vector mask already exists.");
670                         layer.vectorMask = new VectorMask[1];
671                         layer.vectorMask[0].data = null;
672                         layer.vectorMask[0].fileOffset = 0;
673                         ApplyMaskData!VectorMask(maskData[mask], vectorFeather, vectorDensity, &layer.vectorMask[0]);
674                     }
675                     else
676                     {
677                         enforce(layer.layerMask == null, "A layer mask already exists.");
678                         layer.layerMask = new LayerMask[1];
679                         layer.layerMask[0].data = null;
680                         layer.layerMask[0].fileOffset = 0;
681                         ApplyMaskData!LayerMask(maskData[mask], layerFeather, layerDensity, &layer.layerMask[0]);
682                     }
683                 }
684             }
685             
686             // skip blending ranges data, we are not interested in that for now
687             const uint layerBlendingRangesDataLength = file.readValue!uint;
688             file.skip(layerBlendingRangesDataLength);
689 
690             // the layer name is stored as pascal string, padded to a multiple of 4
691             // we peek here as the actual calculation happens inside readPascalStr
692             // TODO: make this more pretty?
693             const ubyte nameLength = file.peekValue!ubyte;
694             const uint paddedNameLength = roundUpToMultiple(nameLength + 1u, 4u);
695 
696             layer.name = file.readPascalStr(paddedNameLength - 1u);
697 
698             // read Additional Layer Information that exists since Photoshop 4.0.
699             // getting the size of this data is a bit awkward, because it's not stored explicitly somewhere. furthermore,
700             // the PSD format sometimes includes the 4-byte length in its section size, and sometimes not.
701             const uint additionalLayerInfoSize = extraDataLength - layerMaskDataLength - layerBlendingRangesDataLength - paddedNameLength - 8u;
702             long toRead = additionalLayerInfoSize;
703 
704             while (toRead > 0)
705             {
706                 const string signature = file.readStr(4);
707                 enforce(signature == "8BIM", "Additional Layer Information section seems to be corrupt, signature does not match \"8BIM\". (was \"%s\")".format(signature));
708 
709                 const string key = file.readStr(4);
710 
711                 // length needs to be rounded to an even number
712                 uint length = file.readValue!uint;
713                 length = roundUpToMultiple(length, 2u);
714 
715                 // read "Section divider setting" to identify whether a layer is a group, or a section divider
716                 if (key == "lsct")
717                 {
718                     layer.type = cast(LayerType)file.readValue!uint;
719 
720                     // skip the rest of the data
721                     file.skip(length - 4u);
722                 }
723                 // read Unicode layer name
724                 else if (key == "luni")
725                 {
726                     // PSD Unicode strings store 4 bytes for the number of characters, NOT bytes, followed by
727                     // 2-byte UTF16 Unicode data without the terminating null.
728                     const uint characterCountWithoutNull = file.readValue!uint;
729                     wstring utf16Name;
730                     for (uint c = 0u; c < characterCountWithoutNull; ++c)
731                     {
732                         utf16Name ~= cast(wchar)file.readValue!ushort;
733                     }
734 
735                     // If there's a unicode name we may as well use that here.
736                     import std.utf : toUTF8;
737                     layer.name = utf16Name.toUTF8;
738 
739                     // Some PSD exporters throw an extra null in there for good measure, yeet it.
740                     if (layer.name[$-1] == '\0') layer.name.length--;
741 
742                     // skip possible padding bytes
743                     file.skip(length - 4u - characterCountWithoutNull * ushort.sizeof);
744                 }
745                 else
746                 {
747                     file.skip(length);
748                 }
749 
750                 toRead -= 3*uint.sizeof + length;
751             }
752 
753             layerMaskSection.layers[i] = layer;
754         }
755 
756 
757         // walk through the layers and channels, but don't extract their data just yet. only save the file offset for extracting the
758         // data later.
759         foreach (i; 0..layerMaskSection.layers.length)
760         {            
761             Layer* layer = &layerMaskSection.layers[i];
762             foreach(j; 0..layer.channels.length) {
763                 ChannelInfo* channel = &layer.channels[j];
764                 channel.fileOffset = cast(uint)file.tell();
765                 file.skip(channel.dataLength);
766             }
767         }
768     }
769 
770     if (sectionLength > 0)
771     {
772         // start loading at the global layer mask info section, located after the Layer Information Section.
773         // note that the 4 bytes that stored the length of the section are not included in the length itself.
774         const ulong globalInfoSectionOffset = sectionOffset + layerLength + 4u;
775         file.seek(globalInfoSectionOffset);
776 
777         // work out how many bytes are left to read at this point. we need that to figure out the size of the last
778         // optional section, the Additional Layer Information.
779         if (sectionOffset + sectionLength > globalInfoSectionOffset)
780         {
781             long toRead = cast(long)(sectionOffset + sectionLength - globalInfoSectionOffset);
782             const uint globalLayerMaskLength = file.readValue!uint;
783             toRead -= uint.sizeof;
784 
785             if (globalLayerMaskLength != 0)
786             {
787                 layerMaskSection.overlayColorSpace = file.readValue!ushort;
788 
789                 // 4*2 byte color components
790                 file.skip(8);
791 
792                 layerMaskSection.opacity = file.readValue!ushort;
793                 layerMaskSection.kind = file.readValue!ubyte;
794 
795                 toRead -= 2u*ushort.sizeof + ubyte.sizeof + 8u;
796 
797                 // filler bytes (zeroes)
798                 const uint remaining = cast(uint)(globalLayerMaskLength - 2u*ushort.sizeof - ubyte.sizeof - 8u);
799                 file.skip(remaining);
800 
801                 toRead -= remaining;
802             }
803 
804             // are there still bytes left to read? then this is the Additional Layer Information that exists since Photoshop 4.0.
805             while (toRead > 0)
806             {
807                 const string signature = file.readStr(4);
808                 enforce(signature == "8BIM", "Additional Layer Information section seems to be corrupt, signature does not match \"8BIM\".");
809 
810                 const string key = file.readStr(4);
811 
812                 // again, length is rounded to a multiple of 4
813                 uint length = file.readValue!uint;
814                 length = roundUpToMultiple(length, 4u);
815 
816                 if (key == "Lr16")
817                 {
818                     const ulong offset = file.tell();
819                     
820                     // NOTE: I think in D we...can just let this get copied over?
821                     //DestroyLayerMaskSection(layerMaskSection, allocator);
822                     layerMaskSection = parseLayer(file, psd, 0u, 0u, length);
823                     file.seek(offset + length);
824                 }
825                 else if (key == "Lr32")
826                 {
827                     const ulong offset = file.tell();
828                     
829                     // NOTE: I think in D we...can just let this get copied over?
830                     //DestroyLayerMaskSection(layerMaskSection, allocator);
831 
832                     layerMaskSection = parseLayer(file, psd, 0u, 0u, length);
833                     file.seek(offset + length);
834                 }
835                 else if (key == "vmsk")
836                 {
837                     // TODO: could read extra vector mask data here
838                     file.skip(length);
839                 }
840                 else if (key == "lnk2")
841                 {
842                     // TODO: could read individual smart object layer data here
843                     file.skip(length);
844                 }
845                 else
846                 {
847                     file.skip(length);
848                 }
849 
850                 toRead -= 3u*uint.sizeof + length;
851             }
852         }
853     }
854 
855     return layerMaskSection;
856 }
857 
858 
859 /*
860                                 IMAGE DATA
861 */
862 void parseImageDataSectionOffset(ref File file, ref PSD psd) {
863     psd.imageDataSectionOffset = file.tell();
864     psd.imageDataSectionLength = file.size() - psd.imageDataSectionOffset;
865 
866     // TODO: read
867 }