20 {
21
22
23 $ThisFileInfo['bmp']['header']['raw'] = array();
24 $thisfile_bmp = &$ThisFileInfo['bmp'];
25 $thisfile_bmp_header = &$thisfile_bmp['header'];
26 $thisfile_bmp_header_raw = &$thisfile_bmp_header['raw'];
27
28
29
30
31
32
33
34
35
36 fseek($fd, $ThisFileInfo[
'avdataoffset'], SEEK_SET);
37 $offset = 0;
38 $BMPheader =
fread($fd, 14 + 40);
39
40 $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
41 $offset += 2;
42
43 if ($thisfile_bmp_header_raw['identifier'] != 'BM') {
44 $ThisFileInfo['error'][] = 'Expecting "BM" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_bmp_header_raw['identifier'].'"';
45 unset($ThisFileInfo['fileformat']);
46 unset($ThisFileInfo['bmp']);
47 return false;
48 }
49
51 $offset += 4;
53 $offset += 2;
55 $offset += 2;
57 $offset += 4;
59 $offset += 4;
60
61
62
65 if (($planes22 == 1) && ($planes26 != 1)) {
66 $thisfile_bmp['type_os'] = 'OS/2';
67 $thisfile_bmp['type_version'] = 1;
68 } elseif (($planes26 == 1) && ($planes22 != 1)) {
69 $thisfile_bmp['type_os'] = 'Windows';
70 $thisfile_bmp['type_version'] = 1;
71 } elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
72 $thisfile_bmp['type_os'] = 'OS/2';
73 $thisfile_bmp['type_version'] = 1;
74 } elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
75 $thisfile_bmp['type_os'] = 'Windows';
76 $thisfile_bmp['type_version'] = 1;
77 } elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
78 $thisfile_bmp['type_os'] = 'Windows';
79 $thisfile_bmp['type_version'] = 4;
80 } elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
81 $thisfile_bmp['type_os'] = 'Windows';
82 $thisfile_bmp['type_version'] = 5;
83 } else {
84 $ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)';
85 unset($ThisFileInfo['fileformat']);
86 unset($ThisFileInfo['bmp']);
87 return false;
88 }
89
90 $ThisFileInfo['fileformat'] = 'bmp';
91 $ThisFileInfo['video']['dataformat'] = 'bmp';
92 $ThisFileInfo['video']['lossless'] = true;
93 $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
94
95 if ($thisfile_bmp['type_os'] == 'OS/2') {
96
97
98
99
100
101
102
103
104
105
107 $offset += 2;
109 $offset += 2;
111 $offset += 2;
113 $offset += 2;
114
115 $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
116 $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
117 $ThisFileInfo['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
118 $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
119
120 if ($thisfile_bmp['type_version'] >= 2) {
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
137 $offset += 4;
139 $offset += 4;
141 $offset += 4;
143 $offset += 4;
145 $offset += 4;
147 $offset += 4;
149 $offset += 2;
151 $offset += 2;
153 $offset += 2;
155 $offset += 2;
157 $offset += 4;
159 $offset += 4;
161 $offset += 4;
163 $offset += 4;
164
166
167 $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
168 }
169
170 } elseif ($thisfile_bmp['type_os'] == 'Windows') {
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
189 $offset += 4;
191 $offset += 4;
193 $offset += 2;
195 $offset += 2;
197 $offset += 4;
199 $offset += 4;
201 $offset += 4;
203 $offset += 4;
205 $offset += 4;
207 $offset += 4;
208
210 $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
211 $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
212 $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
213 $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
214
215 if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
216
217 $BMPheader .=
fread($fd, 44);
218
219
220
221
222
223
224
225
226
227
228
229
231 $offset += 4;
233 $offset += 4;
235 $offset += 4;
237 $offset += 4;
239 $offset += 4;
240 $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4);
241 $offset += 4;
242 $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4);
243 $offset += 4;
244 $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4);
245 $offset += 4;
247 $offset += 4;
249 $offset += 4;
251 $offset += 4;
252
256 }
257
258 if ($thisfile_bmp['type_version'] >= 5) {
259 $BMPheader .=
fread($fd, 16);
260
261
262
263
264
265
266
268 $offset += 4;
270 $offset += 4;
272 $offset += 4;
274 $offset += 4;
275 }
276
277 } else {
278
279 $ThisFileInfo['error'][] = 'Unknown BMP format in header.';
280 return false;
281
282 }
283
284
286 $PaletteEntries = 0;
287 if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
288 $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
289 } elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
290 $PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
291 }
292 if ($PaletteEntries > 0) {
293 $BMPpalette =
fread($fd, 4 * $PaletteEntries);
294 $paletteoffset = 0;
295 for ($i = 0; $i < $PaletteEntries; $i++) {
296
297
298
299
300
304 if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
305
306 } else {
307 $paletteoffset++;
308 }
310 }
311 }
312 }
313
315 fseek($fd, $thisfile_bmp_header_raw[
'data_offset'], SEEK_SET);
316 $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4;
317 $BMPpixelData =
fread($fd, $thisfile_bmp_header_raw[
'height'] * $RowByteLength);
318 $pixeldataoffset = 0;
319 switch (@$thisfile_bmp_header_raw['compression']) {
320
321 case 0:
322 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
323 case 1:
324 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
325 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
326 $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
327 for ($i = 7; $i >= 0; $i--) {
328 $paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
329 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
330 $col++;
331 }
332 }
333 while (($pixeldataoffset % 4) != 0) {
334
335 $pixeldataoffset++;
336 }
337 }
338 break;
339
340 case 4:
341 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
342 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
343 $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
344 for ($i = 1; $i >= 0; $i--) {
345 $paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
346 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
347 $col++;
348 }
349 }
350 while (($pixeldataoffset % 4) != 0) {
351
352 $pixeldataoffset++;
353 }
354 }
355 break;
356
357 case 8:
358 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
359 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
360 $paletteindex = ord($BMPpixelData{$pixeldataoffset++});
361 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
362 }
363 while (($pixeldataoffset % 4) != 0) {
364
365 $pixeldataoffset++;
366 }
367 }
368 break;
369
370 case 24:
371 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
372 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
373 $thisfile_bmp[
'data'][
$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
374 $pixeldataoffset += 3;
375 }
376 while (($pixeldataoffset % 4) != 0) {
377
378 $pixeldataoffset++;
379 }
380 }
381 break;
382
383 case 32:
384 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
385 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
386 $thisfile_bmp[
'data'][
$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
387 $pixeldataoffset += 4;
388 }
389 while (($pixeldataoffset % 4) != 0) {
390
391 $pixeldataoffset++;
392 }
393 }
394 break;
395
396 case 16:
397
398 break;
399
400 default:
401 $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
402 break;
403 }
404 break;
405
406
407 case 1:
408 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
409 case 8:
410 $pixelcounter = 0;
411 while ($pixeldataoffset < strlen($BMPpixelData)) {
414 if ($firstbyte == 0) {
415
416
417
418
419 switch ($secondbyte) {
420 case 0:
421
422
423 break;
424
425 case 1:
426
427 $pixeldataoffset = strlen($BMPpixelData);
428 break;
429
430 case 2:
431
432
433
436 $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
437 $row = ($thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width'])) - $rowincrement;
438 $pixelcounter = (
$row * $thisfile_bmp_header_raw[
'width']) + $col;
439 break;
440
441 default:
442
443
444
445
446 for ($i = 0; $i < $secondbyte; $i++) {
448 $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
449 $row = $thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width']);
450 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
451 $pixelcounter++;
452 }
453 while (($pixeldataoffset % 2) != 0) {
454
455 $pixeldataoffset++;
456 }
457 break;
458 }
459
460 } else {
461
462
463
464 for ($i = 0; $i < $firstbyte; $i++) {
465 $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
466 $row = $thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width']);
467 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$secondbyte];
468 $pixelcounter++;
469 }
470
471 }
472 }
473 break;
474
475 default:
476 $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
477 break;
478 }
479 break;
480
481
482
483 case 2:
484 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
485 case 4:
486 $pixelcounter = 0;
487 while ($pixeldataoffset < strlen($BMPpixelData)) {
490 if ($firstbyte == 0) {
491
492
493
494
495 switch ($secondbyte) {
496 case 0:
497
498
499 break;
500
501 case 1:
502
503 $pixeldataoffset = strlen($BMPpixelData);
504 break;
505
506 case 2:
507
508
509
512 $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
513 $row = ($thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width'])) - $rowincrement;
514 $pixelcounter = (
$row * $thisfile_bmp_header_raw[
'width']) + $col;
515 break;
516
517 default:
518
519
520
521
522 unset($paletteindexes);
523 for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
525 $paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
526 $paletteindexes[] = ($paletteindexbyte & 0x0F);
527 }
528 while (($pixeldataoffset % 2) != 0) {
529
530 $pixeldataoffset++;
531 }
532
533 foreach ($paletteindexes as $paletteindex) {
534 $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
535 $row = $thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width']);
536 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindex];
537 $pixelcounter++;
538 }
539 break;
540 }
541
542 } else {
543
544
545
546
547
548
549
550
551 $paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
552 $paletteindexes[1] = ($secondbyte & 0x0F);
553 for ($i = 0; $i < $firstbyte; $i++) {
554 $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
555 $row = $thisfile_bmp_header_raw[
'height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw[
'width']);
556 $thisfile_bmp[
'data'][
$row][$col] = $thisfile_bmp[
'palette'][$paletteindexes[($i % 2)]];
557 $pixelcounter++;
558 }
559
560 }
561 }
562 break;
563
564 default:
565 $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
566 break;
567 }
568 break;
569
570
571 case 3:
572 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
573 case 16:
574 case 32:
575 $redshift = 0;
576 $greenshift = 0;
577 $blueshift = 0;
578 while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
579 $redshift++;
580 }
581 while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
582 $greenshift++;
583 }
584 while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
585 $blueshift++;
586 }
587 for (
$row = ($thisfile_bmp_header_raw[
'height'] - 1);
$row >= 0;
$row--) {
588 for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
590 $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
591
592 $red = intval(round(((($pixelvalue & $thisfile_bmp_header_raw[
'red_mask']) >> $redshift) / ($thisfile_bmp_header_raw[
'red_mask'] >> $redshift)) * 255));
593 $green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw[
'green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw[
'green_mask'] >> $greenshift)) * 255));
594 $blue = intval(round(((($pixelvalue & $thisfile_bmp_header_raw[
'blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw[
'blue_mask'] >> $blueshift)) * 255));
596 }
597 while (($pixeldataoffset % 4) != 0) {
598
599 $pixeldataoffset++;
600 }
601 }
602 break;
603
604 default:
605 $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
606 break;
607 }
608 break;
609
610
611 default:
612 $ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data';
613 break;
614 }
615 }
616
617 return true;
618 }