@@ -8,6 +8,8 @@ class BmpInfoHeader
8
8
{
9
9
const BITMAPCOREHEADER_SIZE = 12 ;
10
10
const OS21XBITMAPHEADER_SIZE = 12 ;
11
+ const OS22XBITMAPHEADER_MIN_SIZE = 16 ;
12
+ const OS22XBITMAPHEADER_FULL_SIZE = 64 ;
11
13
const BITMAPINFOHEADER_SIZE = 40 ;
12
14
const BITMAPV2INFOHEADER_SIZE = 52 ;
13
15
const BITMAPV3INFOHEADER_SIZE = 56 ;
@@ -40,6 +42,12 @@ class BmpInfoHeader
40
42
public $ greenMask ;
41
43
public $ blueMask ;
42
44
public $ alphaMask ;
45
+ public $ csType ;
46
+ public $ endpoint ;
47
+ public $ gamma ;
48
+ public $ intent ;
49
+ public $ profileData ;
50
+ public $ profileSize ;
43
51
44
52
public function __construct (
45
53
int $ headerSize ,
@@ -59,7 +67,10 @@ public function __construct(
59
67
int $ alphaMask = 0 ,
60
68
int $ csType = 0 ,
61
69
array $ endpoint = [],
62
- array $ gamma = []
70
+ array $ gamma = [],
71
+ int $ intent = 0 ,
72
+ int $ profileData = 0 ,
73
+ int $ profileSize = 0
63
74
) {
64
75
$ this -> headerSize = $ headerSize ;
65
76
$ this -> width = $ width ;
@@ -77,6 +88,12 @@ public function __construct(
77
88
$ this -> greenMask = $ greenMask ;
78
89
$ this -> blueMask = $ blueMask ;
79
90
$ this -> alphaMask = $ alphaMask ;
91
+ $ this -> csType = $ csType ;
92
+ $ this -> endpoint = $ endpoint ;
93
+ $ this -> gamma = $ gamma ;
94
+ $ this -> intent = $ intent ;
95
+ $ this -> profileData = $ profileData ;
96
+ $ this -> profileSize = $ profileSize ;
80
97
}
81
98
82
99
public static function fromBinary (DataInputStream $ data ) : BmpInfoHeader
@@ -86,10 +103,10 @@ public static function fromBinary(DataInputStream $data) : BmpInfoHeader
86
103
switch ($ infoHeaderSize ) {
87
104
case self ::BITMAPCOREHEADER_SIZE :
88
105
return self ::readCoreHeader ($ data );
89
- case 64 :
90
- return self ::readOs22xBitmapHeader ( $ data );
91
- case 16 :
92
- throw new Exception ( " OS22XBITMAPHEADER not implemented " );
106
+ case self :: OS22XBITMAPHEADER_MIN_SIZE :
107
+ case self ::OS22XBITMAPHEADER_FULL_SIZE :
108
+ // OS/2 v2 bitmap header is technically variable-length, only 16 and 64 are used in practice.
109
+ return self :: readOs22xBitmapHeader ( $ infoHeaderSize , $ data );
93
110
case self ::BITMAPINFOHEADER_SIZE :
94
111
return self ::readBitmapInfoHeader ($ data );
95
112
case self ::BITMAPV2INFOHEADER_SIZE :
@@ -165,7 +182,7 @@ private static function getV5fields(DataInputStream $data) : array
165
182
166
183
private static function readBitmapInfoHeader (DataInputStream $ data ) : BmpInfoHeader
167
184
{
168
- $ headerSize = self :: BITMAPINFOHEADER_SIZE ;
185
+ $ extraBytes = 0 ;
169
186
$ infoFields = self ::getInfoFields ($ data );
170
187
// Quirk- A BITMAPINFOHEADER specifying B1_BITFIELDS has 12 bytes of masks after it.
171
188
// In later versions, this information is part of the header itself, and is read unconditionally.
@@ -179,16 +196,16 @@ private static function readBitmapInfoHeader(DataInputStream $data) : BmpInfoHea
179
196
$ redMask = $ rgbMaskFields ['redMask ' ];
180
197
$ greenMask = $ rgbMaskFields ['greenMask ' ];
181
198
$ blueMask = $ rgbMaskFields ['blueMask ' ];
182
- $ headerSize += 12 ;
199
+ $ extraBytes += 12 ;
183
200
}
184
201
if ($ infoFields ['compression ' ] === self ::B1_ALPHABITFIELDS ) {
185
202
// we might or might not need to read a 4-byte alpha mask too, depending on the compression type.
186
203
$ alphaMaskFields = self ::getV3fields ($ data );
187
204
$ alphaMask = $ alphaMaskFields ['alphaMask ' ];
188
- $ headerSize += 4 ;
205
+ $ extraBytes += 4 ;
189
206
}
190
207
return new BmpInfoHeader (
191
- $ headerSize ,
208
+ self :: BITMAPINFOHEADER_SIZE + $ extraBytes , // Count any extra bytes as part of the header
192
209
$ infoFields ['width ' ],
193
210
$ infoFields ['height ' ],
194
211
$ infoFields ['planes ' ],
@@ -289,9 +306,6 @@ private static function readBitmapV5Header(DataInputStream $data) : BmpInfoHeade
289
306
$ v3fields = self ::getV3fields ($ data );
290
307
$ v4fields = self ::getV4fields ($ data );
291
308
$ v5fields = self ::getV5fields ($ data );
292
- if ($ v5fields ['profileSize ' ] > 0 ) { // TODO include these fields
293
- throw new Exception ("Bitmaps with embedded ICC profile data are not supported. " );
294
- }
295
309
return new BmpInfoHeader (
296
310
self ::BITMAPV5HEADER_SIZE ,
297
311
$ infoFields ['width ' ],
@@ -310,12 +324,41 @@ private static function readBitmapV5Header(DataInputStream $data) : BmpInfoHeade
310
324
$ v3fields ['alphaMask ' ],
311
325
$ v4fields ['csType ' ],
312
326
$ v4fields ['endpoint ' ],
313
- $ v4fields ['gamma ' ]
327
+ $ v4fields ['gamma ' ],
328
+ $ v5fields ['intent ' ],
329
+ $ v5fields ['profileData ' ],
330
+ $ v5fields ['profileSize ' ]
314
331
);
315
332
}
316
333
317
- private static function readOs22xBitmapHeader (DataInputStream $ data )
334
+ private static function readOs22xBitmapHeader (int $ size , DataInputStream $ data )
318
335
{
319
- throw new Exception ("OS22XBITMAPHEADER not implemented " );
336
+ $ coreData = $ data -> read (self ::OS22XBITMAPHEADER_MIN_SIZE - 4 );
337
+ $ coreFields = unpack ("Vwidth/Vheight/vplanes/vbpp " , $ coreData );
338
+ if ($ size == self ::OS22XBITMAPHEADER_MIN_SIZE ) {
339
+ return new BmpInfoHeader (
340
+ self ::OS22XBITMAPHEADER_MIN_SIZE ,
341
+ $ coreFields ['width ' ],
342
+ $ coreFields ['height ' ],
343
+ $ coreFields ['planes ' ],
344
+ $ coreFields ['bpp ' ]
345
+ );
346
+ }
347
+ // Read up to the full header size
348
+ $ extraData = $ data -> read (self ::OS22XBITMAPHEADER_FULL_SIZE - self ::OS22XBITMAPHEADER_MIN_SIZE );
349
+ $ extraFields = unpack ("Vcompression/VcompressedSize/VhorizontalRes/VverticalRes/Vcolors/VimportantColors/vunits/vreserved/vrecording/vrendering/Vsize1/Vsize2/VcolorEncoding/Videntifier " , $ extraData );
350
+ return new BmpInfoHeader (
351
+ self ::OS22XBITMAPHEADER_FULL_SIZE ,
352
+ $ coreFields ['width ' ],
353
+ $ coreFields ['height ' ],
354
+ $ coreFields ['planes ' ],
355
+ $ coreFields ['bpp ' ],
356
+ $ extraFields ['compression ' ],
357
+ $ extraFields ['compressedSize ' ],
358
+ $ extraFields ['horizontalRes ' ],
359
+ $ extraFields ['verticalRes ' ],
360
+ $ extraFields ['colors ' ],
361
+ $ extraFields ['importantColors ' ]
362
+ ); // Other fields are ignored.
320
363
}
321
364
}
0 commit comments