ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
module.graphic.bmp.php
Go to the documentation of this file.
1 <?php
4 // available at http://getid3.sourceforge.net //
5 // or http://www.getid3.org //
6 // also https://github.com/JamesHeinrich/getID3 //
8 // See readme.txt for more details //
10 // //
11 // module.graphic.bmp.php //
12 // module for analyzing BMP Image files //
13 // dependencies: NONE //
14 // ///
16 
17 
19 {
20  public $ExtractPalette = false;
21  public $ExtractData = false;
22 
23  public function Analyze() {
24  $info = &$this->getid3->info;
25 
26  // shortcuts
27  $info['bmp']['header']['raw'] = array();
28  $thisfile_bmp = &$info['bmp'];
29  $thisfile_bmp_header = &$thisfile_bmp['header'];
30  $thisfile_bmp_header_raw = &$thisfile_bmp_header['raw'];
31 
32  // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
33  // all versions
34  // WORD bfType;
35  // DWORD bfSize;
36  // WORD bfReserved1;
37  // WORD bfReserved2;
38  // DWORD bfOffBits;
39 
40  $this->fseek($info['avdataoffset']);
41  $offset = 0;
42  $BMPheader = $this->fread(14 + 40);
43 
44  $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
45  $offset += 2;
46 
47  $magic = 'BM';
48  if ($thisfile_bmp_header_raw['identifier'] != $magic) {
49  $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_bmp_header_raw['identifier']).'"');
50  unset($info['fileformat']);
51  unset($info['bmp']);
52  return false;
53  }
54 
55  $thisfile_bmp_header_raw['filesize'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
56  $offset += 4;
57  $thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
58  $offset += 2;
59  $thisfile_bmp_header_raw['reserved2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
60  $offset += 2;
61  $thisfile_bmp_header_raw['data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
62  $offset += 4;
63  $thisfile_bmp_header_raw['header_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
64  $offset += 4;
65 
66 
67  // check if the hardcoded-to-1 "planes" is at offset 22 or 26
68  $planes22 = getid3_lib::LittleEndian2Int(substr($BMPheader, 22, 2));
69  $planes26 = getid3_lib::LittleEndian2Int(substr($BMPheader, 26, 2));
70  if (($planes22 == 1) && ($planes26 != 1)) {
71  $thisfile_bmp['type_os'] = 'OS/2';
72  $thisfile_bmp['type_version'] = 1;
73  } elseif (($planes26 == 1) && ($planes22 != 1)) {
74  $thisfile_bmp['type_os'] = 'Windows';
75  $thisfile_bmp['type_version'] = 1;
76  } elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
77  $thisfile_bmp['type_os'] = 'OS/2';
78  $thisfile_bmp['type_version'] = 1;
79  } elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
80  $thisfile_bmp['type_os'] = 'Windows';
81  $thisfile_bmp['type_version'] = 1;
82  } elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
83  $thisfile_bmp['type_os'] = 'Windows';
84  $thisfile_bmp['type_version'] = 4;
85  } elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
86  $thisfile_bmp['type_os'] = 'Windows';
87  $thisfile_bmp['type_version'] = 5;
88  } else {
89  $this->error('Unknown BMP subtype (or not a BMP file)');
90  unset($info['fileformat']);
91  unset($info['bmp']);
92  return false;
93  }
94 
95  $info['fileformat'] = 'bmp';
96  $info['video']['dataformat'] = 'bmp';
97  $info['video']['lossless'] = true;
98  $info['video']['pixel_aspect_ratio'] = (float) 1;
99 
100  if ($thisfile_bmp['type_os'] == 'OS/2') {
101 
102  // OS/2-format BMP
103  // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
104 
105  // DWORD Size; /* Size of this structure in bytes */
106  // DWORD Width; /* Bitmap width in pixels */
107  // DWORD Height; /* Bitmap height in pixel */
108  // WORD NumPlanes; /* Number of bit planes (color depth) */
109  // WORD BitsPerPixel; /* Number of bits per pixel per plane */
110 
111  $thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
112  $offset += 2;
113  $thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
114  $offset += 2;
115  $thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
116  $offset += 2;
117  $thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
118  $offset += 2;
119 
120  $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
121  $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
122  $info['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
123  $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
124 
125  if ($thisfile_bmp['type_version'] >= 2) {
126  // DWORD Compression; /* Bitmap compression scheme */
127  // DWORD ImageDataSize; /* Size of bitmap data in bytes */
128  // DWORD XResolution; /* X resolution of display device */
129  // DWORD YResolution; /* Y resolution of display device */
130  // DWORD ColorsUsed; /* Number of color table indices used */
131  // DWORD ColorsImportant; /* Number of important color indices */
132  // WORD Units; /* Type of units used to measure resolution */
133  // WORD Reserved; /* Pad structure to 4-byte boundary */
134  // WORD Recording; /* Recording algorithm */
135  // WORD Rendering; /* Halftoning algorithm used */
136  // DWORD Size1; /* Reserved for halftoning algorithm use */
137  // DWORD Size2; /* Reserved for halftoning algorithm use */
138  // DWORD ColorEncoding; /* Color model used in bitmap */
139  // DWORD Identifier; /* Reserved for application use */
140 
141  $thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
142  $offset += 4;
143  $thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
144  $offset += 4;
145  $thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
146  $offset += 4;
147  $thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
148  $offset += 4;
149  $thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
150  $offset += 4;
151  $thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
152  $offset += 4;
153  $thisfile_bmp_header_raw['resolution_units'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
154  $offset += 2;
155  $thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
156  $offset += 2;
157  $thisfile_bmp_header_raw['recording'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
158  $offset += 2;
159  $thisfile_bmp_header_raw['rendering'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
160  $offset += 2;
161  $thisfile_bmp_header_raw['size1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
162  $offset += 4;
163  $thisfile_bmp_header_raw['size2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
164  $offset += 4;
165  $thisfile_bmp_header_raw['color_encoding'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
166  $offset += 4;
167  $thisfile_bmp_header_raw['identifier'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
168  $offset += 4;
169 
170  $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
171 
172  $info['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
173  }
174 
175  } elseif ($thisfile_bmp['type_os'] == 'Windows') {
176 
177  // Windows-format BMP
178 
179  // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
180  // all versions
181  // DWORD biSize;
182  // LONG biWidth;
183  // LONG biHeight;
184  // WORD biPlanes;
185  // WORD biBitCount;
186  // DWORD biCompression;
187  // DWORD biSizeImage;
188  // LONG biXPelsPerMeter;
189  // LONG biYPelsPerMeter;
190  // DWORD biClrUsed;
191  // DWORD biClrImportant;
192 
193  // possibly integrate this section and module.audio-video.riff.php::ParseBITMAPINFOHEADER() ?
194 
195  $thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
196  $offset += 4;
197  $thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
198  $offset += 4;
199  $thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
200  $offset += 2;
201  $thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
202  $offset += 2;
203  $thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
204  $offset += 4;
205  $thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
206  $offset += 4;
207  $thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
208  $offset += 4;
209  $thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
210  $offset += 4;
211  $thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
212  $offset += 4;
213  $thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
214  $offset += 4;
215 
216  $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
217  $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
218  $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
219  $info['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
220  $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
221 
222  if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
223  // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
224  $BMPheader .= $this->fread(44);
225 
226  // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
227  // Win95+, WinNT4.0+
228  // DWORD bV4RedMask;
229  // DWORD bV4GreenMask;
230  // DWORD bV4BlueMask;
231  // DWORD bV4AlphaMask;
232  // DWORD bV4CSType;
233  // CIEXYZTRIPLE bV4Endpoints;
234  // DWORD bV4GammaRed;
235  // DWORD bV4GammaGreen;
236  // DWORD bV4GammaBlue;
237  $thisfile_bmp_header_raw['red_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
238  $offset += 4;
239  $thisfile_bmp_header_raw['green_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
240  $offset += 4;
241  $thisfile_bmp_header_raw['blue_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
242  $offset += 4;
243  $thisfile_bmp_header_raw['alpha_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
244  $offset += 4;
245  $thisfile_bmp_header_raw['cs_type'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
246  $offset += 4;
247  $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4);
248  $offset += 4;
249  $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4);
250  $offset += 4;
251  $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4);
252  $offset += 4;
253  $thisfile_bmp_header_raw['gamma_red'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
254  $offset += 4;
255  $thisfile_bmp_header_raw['gamma_green'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
256  $offset += 4;
257  $thisfile_bmp_header_raw['gamma_blue'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
258  $offset += 4;
259 
260  $thisfile_bmp_header['ciexyz_red'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
261  $thisfile_bmp_header['ciexyz_green'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
262  $thisfile_bmp_header['ciexyz_blue'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
263  }
264 
265  if ($thisfile_bmp['type_version'] >= 5) {
266  $BMPheader .= $this->fread(16);
267 
268  // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
269  // Win98+, Win2000+
270  // DWORD bV5Intent;
271  // DWORD bV5ProfileData;
272  // DWORD bV5ProfileSize;
273  // DWORD bV5Reserved;
274  $thisfile_bmp_header_raw['intent'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
275  $offset += 4;
276  $thisfile_bmp_header_raw['profile_data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
277  $offset += 4;
278  $thisfile_bmp_header_raw['profile_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
279  $offset += 4;
280  $thisfile_bmp_header_raw['reserved3'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
281  $offset += 4;
282  }
283 
284  } else {
285 
286  $this->error('Unknown BMP format in header.');
287  return false;
288 
289  }
290 
291 
292  if ($this->ExtractPalette || $this->ExtractData) {
293  $PaletteEntries = 0;
294  if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
295  $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
296  } elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
297  $PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
298  }
299  if ($PaletteEntries > 0) {
300  $BMPpalette = $this->fread(4 * $PaletteEntries);
301  $paletteoffset = 0;
302  for ($i = 0; $i < $PaletteEntries; $i++) {
303  // RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
304  // BYTE rgbBlue;
305  // BYTE rgbGreen;
306  // BYTE rgbRed;
307  // BYTE rgbReserved;
308  $blue = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
309  $green = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
310  $red = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
311  if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
312  // no padding byte
313  } else {
314  $paletteoffset++; // padding byte
315  }
316  $thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | $blue);
317  }
318  }
319  }
320 
321  if ($this->ExtractData) {
322  $this->fseek($thisfile_bmp_header_raw['data_offset']);
323  $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
324  $BMPpixelData = $this->fread($thisfile_bmp_header_raw['height'] * $RowByteLength);
325  $pixeldataoffset = 0;
326  $thisfile_bmp_header_raw['compression'] = (isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : '');
327  switch ($thisfile_bmp_header_raw['compression']) {
328 
329  case 0: // BI_RGB
330  switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
331  case 1:
332  for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
333  for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
334  $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
335  for ($i = 7; $i >= 0; $i--) {
336  $paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
337  $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
338  $col++;
339  }
340  }
341  while (($pixeldataoffset % 4) != 0) {
342  // lines are padded to nearest DWORD
343  $pixeldataoffset++;
344  }
345  }
346  break;
347 
348  case 4:
349  for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
350  for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
351  $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
352  for ($i = 1; $i >= 0; $i--) {
353  $paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
354  $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
355  $col++;
356  }
357  }
358  while (($pixeldataoffset % 4) != 0) {
359  // lines are padded to nearest DWORD
360  $pixeldataoffset++;
361  }
362  }
363  break;
364 
365  case 8:
366  for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
367  for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
368  $paletteindex = ord($BMPpixelData{$pixeldataoffset++});
369  $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
370  }
371  while (($pixeldataoffset % 4) != 0) {
372  // lines are padded to nearest DWORD
373  $pixeldataoffset++;
374  }
375  }
376  break;
377 
378  case 24:
379  for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
380  for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
381  $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
382  $pixeldataoffset += 3;
383  }
384  while (($pixeldataoffset % 4) != 0) {
385  // lines are padded to nearest DWORD
386  $pixeldataoffset++;
387  }
388  }
389  break;
390 
391  case 32:
392  for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
393  for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
394  $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
395  $pixeldataoffset += 4;
396  }
397  while (($pixeldataoffset % 4) != 0) {
398  // lines are padded to nearest DWORD
399  $pixeldataoffset++;
400  }
401  }
402  break;
403 
404  case 16:
405  // ?
406  break;
407 
408  default:
409  $this->error('Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data');
410  break;
411  }
412  break;
413 
414 
415  case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
416  switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
417  case 8:
418  $pixelcounter = 0;
419  while ($pixeldataoffset < strlen($BMPpixelData)) {
420  $firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
421  $secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
422  if ($firstbyte == 0) {
423 
424  // escaped/absolute mode - the first byte of the pair can be set to zero to
425  // indicate an escape character that denotes the end of a line, the end of
426  // a bitmap, or a delta, depending on the value of the second byte.
427  switch ($secondbyte) {
428  case 0:
429  // end of line
430  // no need for special processing, just ignore
431  break;
432 
433  case 1:
434  // end of bitmap
435  $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
436  break;
437 
438  case 2:
439  // delta - The 2 bytes following the escape contain unsigned values
440  // indicating the horizontal and vertical offsets of the next pixel
441  // from the current position.
442  $colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
443  $rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
444  $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
445  $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
446  $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
447  break;
448 
449  default:
450  // In absolute mode, the first byte is zero and the second byte is a
451  // value in the range 03H through FFH. The second byte represents the
452  // number of bytes that follow, each of which contains the color index
453  // of a single pixel. Each run must be aligned on a word boundary.
454  for ($i = 0; $i < $secondbyte; $i++) {
455  $paletteindex = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
456  $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
457  $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
458  $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
459  $pixelcounter++;
460  }
461  while (($pixeldataoffset % 2) != 0) {
462  // Each run must be aligned on a word boundary.
463  $pixeldataoffset++;
464  }
465  break;
466  }
467 
468  } else {
469 
470  // encoded mode - the first byte specifies the number of consecutive pixels
471  // to be drawn using the color index contained in the second byte.
472  for ($i = 0; $i < $firstbyte; $i++) {
473  $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
474  $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
475  $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
476  $pixelcounter++;
477  }
478 
479  }
480  }
481  break;
482 
483  default:
484  $this->error('Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data');
485  break;
486  }
487  break;
488 
489 
490 
491  case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
492  switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
493  case 4:
494  $pixelcounter = 0;
495  while ($pixeldataoffset < strlen($BMPpixelData)) {
496  $firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
497  $secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
498  if ($firstbyte == 0) {
499 
500  // escaped/absolute mode - the first byte of the pair can be set to zero to
501  // indicate an escape character that denotes the end of a line, the end of
502  // a bitmap, or a delta, depending on the value of the second byte.
503  switch ($secondbyte) {
504  case 0:
505  // end of line
506  // no need for special processing, just ignore
507  break;
508 
509  case 1:
510  // end of bitmap
511  $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
512  break;
513 
514  case 2:
515  // delta - The 2 bytes following the escape contain unsigned values
516  // indicating the horizontal and vertical offsets of the next pixel
517  // from the current position.
518  $colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
519  $rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
520  $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
521  $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
522  $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
523  break;
524 
525  default:
526  // In absolute mode, the first byte is zero. The second byte contains the number
527  // of color indexes that follow. Subsequent bytes contain color indexes in their
528  // high- and low-order 4 bits, one color index for each pixel. In absolute mode,
529  // each run must be aligned on a word boundary.
530  unset($paletteindexes);
531  for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
532  $paletteindexbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
533  $paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
534  $paletteindexes[] = ($paletteindexbyte & 0x0F);
535  }
536  while (($pixeldataoffset % 2) != 0) {
537  // Each run must be aligned on a word boundary.
538  $pixeldataoffset++;
539  }
540 
541  foreach ($paletteindexes as $paletteindex) {
542  $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
543  $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
544  $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
545  $pixelcounter++;
546  }
547  break;
548  }
549 
550  } else {
551 
552  // encoded mode - the first byte of the pair contains the number of pixels to be
553  // drawn using the color indexes in the second byte. The second byte contains two
554  // color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
555  // The first of the pixels is drawn using the color specified by the high-order
556  // 4 bits, the second is drawn using the color in the low-order 4 bits, the third
557  // is drawn using the color in the high-order 4 bits, and so on, until all the
558  // pixels specified by the first byte have been drawn.
559  $paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
560  $paletteindexes[1] = ($secondbyte & 0x0F);
561  for ($i = 0; $i < $firstbyte; $i++) {
562  $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
563  $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
564  $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]];
565  $pixelcounter++;
566  }
567 
568  }
569  }
570  break;
571 
572  default:
573  $this->error('Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data');
574  break;
575  }
576  break;
577 
578 
579  case 3: // BI_BITFIELDS
580  switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
581  case 16:
582  case 32:
583  $redshift = 0;
584  $greenshift = 0;
585  $blueshift = 0;
586  while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
587  $redshift++;
588  }
589  while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
590  $greenshift++;
591  }
592  while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
593  $blueshift++;
594  }
595  for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
596  for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
597  $pixelvalue = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
598  $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
599 
600  $red = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift)) * 255));
601  $green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255));
602  $blue = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift)) * 255));
603  $thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue));
604  }
605  while (($pixeldataoffset % 4) != 0) {
606  // lines are padded to nearest DWORD
607  $pixeldataoffset++;
608  }
609  }
610  break;
611 
612  default:
613  $this->error('Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data');
614  break;
615  }
616  break;
617 
618 
619  default: // unhandled compression type
620  $this->error('Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data');
621  break;
622  }
623  }
624 
625  return true;
626  }
627 
628 
629  public function PlotBMP(&$BMPinfo) {
630  $starttime = time();
631  if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) {
632  echo 'ERROR: no pixel data<BR>';
633  return false;
634  }
635  set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000)));
636  if ($im = ImageCreateTrueColor($BMPinfo['resolution_x'], $BMPinfo['resolution_y'])) {
637  for ($row = 0; $row < $BMPinfo['resolution_y']; $row++) {
638  for ($col = 0; $col < $BMPinfo['resolution_x']; $col++) {
639  if (isset($BMPinfo['bmp']['data'][$row][$col])) {
640  $red = ($BMPinfo['bmp']['data'][$row][$col] & 0x00FF0000) >> 16;
641  $green = ($BMPinfo['bmp']['data'][$row][$col] & 0x0000FF00) >> 8;
642  $blue = ($BMPinfo['bmp']['data'][$row][$col] & 0x000000FF);
643  $pixelcolor = ImageColorAllocate($im, $red, $green, $blue);
644  ImageSetPixel($im, $col, $row, $pixelcolor);
645  } else {
646  //echo 'ERROR: no data for pixel '.$row.' x '.$col.'<BR>';
647  //return false;
648  }
649  }
650  }
651  if (headers_sent()) {
652  echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds<BR>';
653  ImageDestroy($im);
654  exit;
655  } else {
656  header('Content-type: image/png');
657  ImagePNG($im);
658  ImageDestroy($im);
659  return true;
660  }
661  }
662  return false;
663  }
664 
665  public function BMPcompressionWindowsLookup($compressionid) {
666  static $BMPcompressionWindowsLookup = array(
667  0 => 'BI_RGB',
668  1 => 'BI_RLE8',
669  2 => 'BI_RLE4',
670  3 => 'BI_BITFIELDS',
671  4 => 'BI_JPEG',
672  5 => 'BI_PNG'
673  );
674  return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid');
675  }
676 
677  public function BMPcompressionOS2Lookup($compressionid) {
678  static $BMPcompressionOS2Lookup = array(
679  0 => 'BI_RGB',
680  1 => 'BI_RLE8',
681  2 => 'BI_RLE4',
682  3 => 'Huffman 1D',
683  4 => 'BI_RLE24',
684  );
685  return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid');
686  }
687 
688 }
$blue
Definition: example_030.php:81
error($text)
Definition: getid3.php:1752
PlotBMP(&$BMPinfo)
static LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:292
getID3() by James Heinrich info@getid3.org //
$red
Definition: example_030.php:80
BMPcompressionOS2Lookup($compressionid)
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: getid3.lib.php:18
static FixedPoint2_30($rawdata)
Definition: getid3.lib.php:485
fread($bytes)
Definition: getid3.php:1683
$green
Definition: example_030.php:83
Add a drawing to the header
Definition: 04printing.php:69
Create styles array
The data for the language used.
BMPcompressionWindowsLookup($compressionid)
$i
Definition: disco.tpl.php:19
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1711
$info
Definition: index.php:5