1 module psd.parse.sections.header;
2 import psd.parse;
3 import psd.parse.io;
4 import std.exception;
5 
6 /**
7     PSD Color Modes
8 */
9 enum ColorMode : ushort {
10     Bitmap = 0,
11     Grayscale = 1,
12     Indexed = 2,
13     RGB = 3,
14     CMYK = 4,
15     Multichannel = 7,
16     Duotone = 8,
17     Lab = 9
18 }
19 
20 
21 /**
22     Will read the signature from the header.
23 */
24 pragma(inline, true)
25 char[4] readSignature(ref ParserContext ctx) {
26     char[4] signature;
27 
28     foreach (i; 0 .. 4)
29         signature[i] = ctx.readValue!byte;
30 
31     return signature;
32 }
33 
34 /**
35     Will read the reserve bytes from the header.
36 */
37 pragma(inline, true)
38 char[6] readReserveBytes(ref ParserContext ctx) {
39     char[6] reserve;
40 
41     foreach (i; 0 .. 6)
42         reserve[i] = ctx.readValue!byte;
43 
44     return reserve;
45 }
46 
47 
48 
49 struct PSD_Header {
50     /**
51         Signature of the file
52     */
53     char[4] signature;
54 
55     /**
56         Amount of channels in file
57     */
58     short versionNo;
59 
60     /**
61         Reserved bytes, should be zero
62     */
63     char[6] reserved;
64 
65     /**
66         Amount of channels in file
67     */
68     short channels;
69 
70     /**
71         Width of document
72     */
73     uint width;
74 
75     /**
76         Height of document
77     */
78     uint height;
79 
80     /**
81         Bits per channel
82     */
83     ushort bitsPerChannel;
84 
85     /**
86         Color mode of document
87     */
88     ColorMode colorMode;
89 }
90 
91 /**
92     Parses PSD header
93 */
94 void parseHeader(ref ParserContext ctx) {
95     PSD_Header header;
96     header.signature = readSignature(ctx);
97     byte[4] signatureBytes = [ '8','B','P','S' ];
98 
99     // Make sure that this file has a photoshop header and is the correct version
100     enforce(signatureBytes == header.signature, "Invalid PSD file signature");
101 
102     auto versionNo = ctx.readValue!ushort;
103     enforce((1 == versionNo) || (2 == versionNo), "Invalid PSD or PSB file version");
104     
105     if (2 == versionNo)
106         ctx.psd.psbFile = true;
107     else 
108         ctx.psd.psbFile = false;
109 
110     header.reserved = readReserveBytes(ctx);
111     byte[6] reserveBytes = [ '\0','\0','\0','\0','\0','\0' ];
112     enforce(reserveBytes == header.reserved, "Invalid PSD reserve bytes.");
113 
114     // Read channels
115     header.channels = ctx.readValue!ushort;
116     
117     // NOTE: Photoshop flips width/height order for some reason
118     header.height = ctx.readValue!uint;
119     header.width = ctx.readValue!uint;
120 
121     header.bitsPerChannel = ctx.readValue!ushort;
122     header.colorMode = cast(ColorMode)ctx.readValue!ushort;
123 
124     ctx.psd.header = header;
125 
126     // TODO: Support maybe CMYK
127     enforce(header.colorMode == ColorMode.RGB, "Only RGB/RGBA files are supported currently!");
128 
129 	ctx.psd.colorModeDataSection.length = ctx.readValue!uint;
130     ctx.psd.colorModeDataSection.offset = cast(ulong)ctx.tell();
131     ctx.skip(ctx.psd.colorModeDataSection.length);
132 
133 	ctx.psd.imageResourcesSection.length = ctx.readValue!uint;
134     ctx.psd.imageResourcesSection.offset = cast(ulong)ctx.tell();
135     ctx.skip(ctx.psd.imageResourcesSection.length);
136 
137 	ctx.psd.layerMaskInfoSection.length = ctx.readValue!uint;
138     ctx.psd.layerMaskInfoSection.offset = cast(ulong)ctx.tell();
139     ctx.skip(ctx.psd.layerMaskInfoSection.length);
140 
141 	ctx.psd.imageDataSection.length = cast(uint)(ctx.length() - ctx.tell());
142     ctx.psd.imageDataSection.offset = ctx.tell();
143 }