47 {
48 $info = &$this->getid3->info;
49
50
51 $info[
'bmp'][
'header'][
'raw'] = array();
52 $thisfile_bmp = &
$info[
'bmp'];
53 $thisfile_bmp_header = &$thisfile_bmp['header'];
54 $thisfile_bmp_header_raw = &$thisfile_bmp_header['raw'];
55
56
57
58
59
60
61
62
63
64 fseek($this->getid3->fp,
$info[
'avdataoffset'], SEEK_SET);
65 $offset = 0;
66 $BMPheader =
fread($this->getid3->fp, 14 + 40);
67
68 $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
69 $offset += 2;
70
71 $magic = 'BM';
72 if ($thisfile_bmp_header_raw['identifier'] != $magic) {
73 $info[
'error'][] =
'Expecting "'.Helper::PrintHexBytes($magic).
'" at offset '.
$info[
'avdataoffset'].
', found "'.
Helper::PrintHexBytes($thisfile_bmp_header_raw[
'identifier']).
'"';
74 unset(
$info[
'fileformat']);
76
77 return false;
78 }
79
81 $offset += 4;
83 $offset += 2;
85 $offset += 2;
87 $offset += 4;
89 $offset += 4;
90
91
94 if (($planes22 == 1) && ($planes26 != 1)) {
95 $thisfile_bmp['type_os'] = 'OS/2';
96 $thisfile_bmp['type_version'] = 1;
97 } elseif (($planes26 == 1) && ($planes22 != 1)) {
98 $thisfile_bmp['type_os'] = 'Windows';
99 $thisfile_bmp['type_version'] = 1;
100 } elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
101 $thisfile_bmp['type_os'] = 'OS/2';
102 $thisfile_bmp['type_version'] = 1;
103 } elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
104 $thisfile_bmp['type_os'] = 'Windows';
105 $thisfile_bmp['type_version'] = 1;
106 } elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
107 $thisfile_bmp['type_os'] = 'Windows';
108 $thisfile_bmp['type_version'] = 4;
109 } elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
110 $thisfile_bmp['type_os'] = 'Windows';
111 $thisfile_bmp['type_version'] = 5;
112 } else {
113 $info[
'error'][] =
'Unknown BMP subtype (or not a BMP file)';
114 unset(
$info[
'fileformat']);
116
117 return false;
118 }
119
120 $info[
'fileformat'] =
'bmp';
121 $info[
'video'][
'dataformat'] =
'bmp';
122 $info[
'video'][
'lossless'] =
true;
123 $info[
'video'][
'pixel_aspect_ratio'] = (float) 1;
124
125 if ($thisfile_bmp['type_os'] == 'OS/2') {
126
127
128
129
130
131
132
133
134
135
137 $offset += 2;
139 $offset += 2;
141 $offset += 2;
143 $offset += 2;
144
145 $info[
'video'][
'resolution_x'] = $thisfile_bmp_header_raw[
'width'];
146 $info[
'video'][
'resolution_y'] = $thisfile_bmp_header_raw[
'height'];
147 $info[
'video'][
'codec'] =
'BI_RGB '.$thisfile_bmp_header_raw[
'bits_per_pixel'].
'-bit';
148 $info[
'video'][
'bits_per_sample'] = $thisfile_bmp_header_raw[
'bits_per_pixel'];
149
150 if ($thisfile_bmp['type_version'] >= 2) {
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
167 $offset += 4;
169 $offset += 4;
171 $offset += 4;
173 $offset += 4;
175 $offset += 4;
177 $offset += 4;
179 $offset += 2;
181 $offset += 2;
183 $offset += 2;
185 $offset += 2;
187 $offset += 4;
189 $offset += 4;
191 $offset += 4;
193 $offset += 4;
194
196
197 $info[
'video'][
'codec'] = $thisfile_bmp_header[
'compression'].
' '.$thisfile_bmp_header_raw[
'bits_per_pixel'].
'-bit';
198 }
199
200 } elseif ($thisfile_bmp['type_os'] == 'Windows') {
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
221 $offset += 4;
223 $offset += 4;
225 $offset += 2;
227 $offset += 2;
229 $offset += 4;
231 $offset += 4;
233 $offset += 4;
235 $offset += 4;
237 $offset += 4;
239 $offset += 4;
240
242 $info[
'video'][
'resolution_x'] = $thisfile_bmp_header_raw[
'width'];
243 $info[
'video'][
'resolution_y'] = $thisfile_bmp_header_raw[
'height'];
244 $info[
'video'][
'codec'] = $thisfile_bmp_header[
'compression'].
' '.$thisfile_bmp_header_raw[
'bits_per_pixel'].
'-bit';
245 $info[
'video'][
'bits_per_sample'] = $thisfile_bmp_header_raw[
'bits_per_pixel'];
246
247 if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
248
249 $BMPheader .=
fread($this->getid3->fp, 44);
250
251
252
253
254
255
256
257
258
259
260
261
263 $offset += 4;
265 $offset += 4;
267 $offset += 4;
269 $offset += 4;
271 $offset += 4;
272 $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4);
273 $offset += 4;
274 $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4);
275 $offset += 4;
276 $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4);
277 $offset += 4;
279 $offset += 4;
281 $offset += 4;
283 $offset += 4;
284
285 $thisfile_bmp_header[
'ciexyz_red'] =
Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw[
'ciexyz_red']));
286 $thisfile_bmp_header[
'ciexyz_green'] =
Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw[
'ciexyz_green']));
287 $thisfile_bmp_header[
'ciexyz_blue'] =
Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw[
'ciexyz_blue']));
288 }
289
290 if ($thisfile_bmp['type_version'] >= 5) {
291 $BMPheader .=
fread($this->getid3->fp, 16);
292
293
294
295
296
297
298
300 $offset += 4;
302 $offset += 4;
304 $offset += 4;
306 $offset += 4;
307 }
308
309 } else {
310
311 $info[
'error'][] =
'Unknown BMP format in header.';
312
313 return false;
314
315 }
316
317 if ($this->ExtractPalette || $this->ExtractData) {
318 $PaletteEntries = 0;
319 if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
320 $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
321 } elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
322 $PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
323 }
324 if ($PaletteEntries > 0) {
325 $BMPpalette =
fread($this->getid3->fp, 4 * $PaletteEntries);
326 $paletteoffset = 0;
327 for ($i = 0; $i < $PaletteEntries; $i++) {
328
329
330
331
332
336 if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
337
338 } else {
339 $paletteoffset++;
340 }
342 }
343 }
344 }
345
346 if ($this->ExtractData) {
347 fseek($this->getid3->fp, $thisfile_bmp_header_raw[
'data_offset'], SEEK_SET);
348 $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4;
349 $BMPpixelData =
fread($this->getid3->fp, $thisfile_bmp_header_raw[
'height'] * $RowByteLength);
350 $pixeldataoffset = 0;
351 $thisfile_bmp_header_raw['compression'] = (isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : '');
352 switch ($thisfile_bmp_header_raw['compression']) {
353
354 case 0:
355 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
356 case 1:
357 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
358 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
359 $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
360 for ($i = 7; $i >= 0; $i--) {
361 $paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
362 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
363 $col++;
364 }
365 }
366 while (($pixeldataoffset % 4) != 0) {
367
368 $pixeldataoffset++;
369 }
370 }
371 break;
372
373 case 4:
374 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
375 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
376 $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
377 for ($i = 1; $i >= 0; $i--) {
378 $paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
379 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
380 $col++;
381 }
382 }
383 while (($pixeldataoffset % 4) != 0) {
384
385 $pixeldataoffset++;
386 }
387 }
388 break;
389
390 case 8:
391 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
392 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
393 $paletteindex = ord($BMPpixelData{$pixeldataoffset++});
394 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
395 }
396 while (($pixeldataoffset % 4) != 0) {
397
398 $pixeldataoffset++;
399 }
400 }
401 break;
402
403 case 24:
404 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
405 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
406 $thisfile_bmp[
'data'][
$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
407 $pixeldataoffset += 3;
408 }
409 while (($pixeldataoffset % 4) != 0) {
410
411 $pixeldataoffset++;
412 }
413 }
414 break;
415
416 case 32:
417 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
418 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
419 $thisfile_bmp[
'data'][
$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
420 $pixeldataoffset += 4;
421 }
422 while (($pixeldataoffset % 4) != 0) {
423
424 $pixeldataoffset++;
425 }
426 }
427 break;
428
429 case 16:
430
431 break;
432
433 default:
434 $info[
'error'][] =
'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw[
'bits_per_pixel'].
') - cannot read pixel data';
435 break;
436 }
437 break;
438
439 case 1:
440 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
441 case 8:
442 $pixelcounter = 0;
443 while ($pixeldataoffset < strlen($BMPpixelData)) {
446 if ($firstbyte == 0) {
447
448
449
450
451 switch ($secondbyte) {
452 case 0:
453
454
455 break;
456
457 case 1:
458
459 $pixeldataoffset = strlen($BMPpixelData);
460 break;
461
462 case 2:
463
464
465
468 $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
469 $row = ($thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width'])) - $rowincrement;
470 $pixelcounter = (
$row * $thisfile_bmp_header_raw[
'width']) + $col;
471 break;
472
473 default:
474
475
476
477
478 for ($i = 0; $i < $secondbyte; $i++) {
480 $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
481 $row = $thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width']);
482 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
483 $pixelcounter++;
484 }
485 while (($pixeldataoffset % 2) != 0) {
486
487 $pixeldataoffset++;
488 }
489 break;
490 }
491
492 } else {
493
494
495
496 for ($i = 0; $i < $firstbyte; $i++) {
497 $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
498 $row = $thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width']);
499 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$secondbyte];
500 $pixelcounter++;
501 }
502
503 }
504 }
505 break;
506
507 default:
508 $info[
'error'][] =
'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw[
'bits_per_pixel'].
') - cannot read pixel data';
509 break;
510 }
511 break;
512
513 case 2:
514 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
515 case 4:
516 $pixelcounter = 0;
517 while ($pixeldataoffset < strlen($BMPpixelData)) {
520 if ($firstbyte == 0) {
521
522
523
524
525 switch ($secondbyte) {
526 case 0:
527
528
529 break;
530
531 case 1:
532
533 $pixeldataoffset = strlen($BMPpixelData);
534 break;
535
536 case 2:
537
538
539
542 $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
543 $row = ($thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width'])) - $rowincrement;
544 $pixelcounter = (
$row * $thisfile_bmp_header_raw[
'width']) + $col;
545 break;
546
547 default:
548
549
550
551
552 unset($paletteindexes);
553 for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
555 $paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
556 $paletteindexes[] = ($paletteindexbyte & 0x0F);
557 }
558 while (($pixeldataoffset % 2) != 0) {
559
560 $pixeldataoffset++;
561 }
562
563 foreach ($paletteindexes as $paletteindex) {
564 $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
565 $row = $thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width']);
566 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
567 $pixelcounter++;
568 }
569 break;
570 }
571
572 } else {
573
574
575
576
577
578
579
580
581 $paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
582 $paletteindexes[1] = ($secondbyte & 0x0F);
583 for ($i = 0; $i < $firstbyte; $i++) {
584 $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
585 $row = $thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width']);
586 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindexes[($i % 2)]];
587 $pixelcounter++;
588 }
589
590 }
591 }
592 break;
593
594 default:
595 $info[
'error'][] =
'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw[
'bits_per_pixel'].
') - cannot read pixel data';
596 break;
597 }
598 break;
599
600 case 3:
601 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
602 case 16:
603 case 32:
604 $redshift = 0;
605 $greenshift = 0;
606 $blueshift = 0;
607 while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
608 $redshift++;
609 }
610 while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
611 $greenshift++;
612 }
613 while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
614 $blueshift++;
615 }
616 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
617 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
618 $pixelvalue =
Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw[
'bits_per_pixel'] / 8));
619 $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
620
621 $red = intval(round(((($pixelvalue & $thisfile_bmp_header_raw[
'red_mask']) >> $redshift) / ($thisfile_bmp_header_raw[
'red_mask'] >> $redshift)) * 255));
622 $green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw[
'green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw[
'green_mask'] >> $greenshift)) * 255));
623 $blue = intval(round(((($pixelvalue & $thisfile_bmp_header_raw[
'blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw[
'blue_mask'] >> $blueshift)) * 255));
625 }
626 while (($pixeldataoffset % 4) != 0) {
627
628 $pixeldataoffset++;
629 }
630 }
631 break;
632
633 default:
634 $info[
'error'][] =
'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw[
'bits_per_pixel'].
') - cannot read pixel data';
635 break;
636 }
637 break;
638
639 default:
640 $info[
'error'][] =
'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw[
'compression'].
') - cannot decompress pixel data';
641 break;
642 }
643 }
644
645 return true;
646 }
fseek($bytes, $whence=SEEK_SET)
static LittleEndian2Int($byteword, $signed=false)
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
static FixedPoint2_30($rawdata)
BMPcompressionOS2Lookup($compressionid)
@staticvar array $BMPcompressionOS2Lookup
BMPcompressionWindowsLookup($compressionid)
@staticvar array $BMPcompressionWindowsLookup