root/devel-backup/units/phpthumb/phpthumb.bmp.php

Revision 45, 34.1 kB (checked in by sven, 3 years ago)

dos2unix to clean line endings, set svn property eol-style native, some whitespace homogenisation

  • Property svn:eol-style set to native
Line 
1 <?php
2 /////////////////////////////////////////////////////////////////
3 /// getID3() by James Heinrich <info@getid3.org>               //
4 //  available at http://getid3.sourceforge.net                 //
5 //            or http://www.getid3.org                         //
6 /////////////////////////////////////////////////////////////////
7 // See readme.txt for more details                             //
8 /////////////////////////////////////////////////////////////////
9 //                                                             //
10 // module.graphic.bmp.php                                      //
11 // module for analyzing BMP Image files                        //
12 // dependencies: NONE                                          //
13 //                                                            ///
14 /////////////////////////////////////////////////////////////////
15 //                                                             //
16 // Modified for use in phpThumb() - James Heinrich 2004.07.27  //
17 //                                                             //
18 /////////////////////////////////////////////////////////////////
19
20
21 class phpthumb_bmp {
22
23     function phpthumb_bmp() {
24     }
25
26     function phpthumb_bmp2gd(&$BMPdata, $truecolor=true) {
27         $ThisFileInfo = array();
28         if ($this->getid3_bmp($BMPdata, $ThisFileInfo, true, true)) {
29             $gd = $this->PlotPixelsGD($ThisFileInfo['bmp'], $truecolor);
30             return $gd;
31         }
32         return false;
33     }
34
35     function phpthumb_bmpfile2gd($filename, $truecolor=true) {
36         if ($fp = @fopen($filename, 'rb')) {
37             $BMPdata = fread($fp, filesize($filename));
38             fclose($fp);
39             return $this->phpthumb_bmp2gd($BMPdata, $truecolor);
40         }
41         return false;
42     }
43
44     function getid3_bmp(&$BMPdata, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) {
45
46         // shortcuts
47         $ThisFileInfo['bmp']['header']['raw'] = array();
48         $thisfile_bmp                         = &$ThisFileInfo['bmp'];
49         $thisfile_bmp_header                  = &$thisfile_bmp['header'];
50         $thisfile_bmp_header_raw              = &$thisfile_bmp_header['raw'];
51
52         // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
53         // all versions
54         // WORD    bfType;
55         // DWORD   bfSize;
56         // WORD    bfReserved1;
57         // WORD    bfReserved2;
58         // DWORD   bfOffBits;
59
60         $offset = 0;
61         $overalloffset = 0;
62         $BMPheader = substr($BMPdata, $overalloffset, 14 + 40);
63         $overalloffset += (14 + 40);
64
65         $thisfile_bmp_header_raw['identifier']  = substr($BMPheader, $offset, 2);
66         $offset += 2;
67
68         if ($thisfile_bmp_header_raw['identifier'] != 'BM') {
69             $ThisFileInfo['error'][] = 'Expecting "BM" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_bmp_header_raw['identifier'].'"';
70             unset($ThisFileInfo['fileformat']);
71             unset($ThisFileInfo['bmp']);
72             return false;
73         }
74
75         $thisfile_bmp_header_raw['filesize']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
76         $offset += 4;
77         $thisfile_bmp_header_raw['reserved1']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
78         $offset += 2;
79         $thisfile_bmp_header_raw['reserved2']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
80         $offset += 2;
81         $thisfile_bmp_header_raw['data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
82         $offset += 4;
83         $thisfile_bmp_header_raw['header_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
84         $offset += 4;
85
86
87         // check if the hardcoded-to-1 "planes" is at offset 22 or 26
88         $planes22 = $this->LittleEndian2Int(substr($BMPheader, 22, 2));
89         $planes26 = $this->LittleEndian2Int(substr($BMPheader, 26, 2));
90         if (($planes22 == 1) && ($planes26 != 1)) {
91             $thisfile_bmp['type_os']      = 'OS/2';
92             $thisfile_bmp['type_version'] = 1;
93         } elseif (($planes26 == 1) && ($planes22 != 1)) {
94             $thisfile_bmp['type_os']      = 'Windows';
95             $thisfile_bmp['type_version'] = 1;
96         } elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
97             $thisfile_bmp['type_os']      = 'OS/2';
98             $thisfile_bmp['type_version'] = 1;
99         } elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
100             $thisfile_bmp['type_os']      = 'Windows';
101             $thisfile_bmp['type_version'] = 1;
102         } elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
103             $thisfile_bmp['type_os']      = 'Windows';
104             $thisfile_bmp['type_version'] = 4;
105         } elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
106             $thisfile_bmp['type_os']      = 'Windows';
107             $thisfile_bmp['type_version'] = 5;
108         } else {
109             $ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)';
110             unset($ThisFileInfo['fileformat']);
111             unset($ThisFileInfo['bmp']);
112             return false;
113         }
114
115         $ThisFileInfo['fileformat']                  = 'bmp';
116         $ThisFileInfo['video']['dataformat']         = 'bmp';
117         $ThisFileInfo['video']['lossless']           = true;
118         $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
119
120         if ($thisfile_bmp['type_os'] == 'OS/2') {
121
122             // OS/2-format BMP
123             // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
124
125             // DWORD  Size;             /* Size of this structure in bytes */
126             // DWORD  Width;            /* Bitmap width in pixels */
127             // DWORD  Height;           /* Bitmap height in pixel */
128             // WORD   NumPlanes;        /* Number of bit planes (color depth) */
129             // WORD   BitsPerPixel;     /* Number of bits per pixel per plane */
130
131             $thisfile_bmp_header_raw['width']          = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
132             $offset += 2;
133             $thisfile_bmp_header_raw['height']         = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
134             $offset += 2;
135             $thisfile_bmp_header_raw['planes']         = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
136             $offset += 2;
137             $thisfile_bmp_header_raw['bits_per_pixel'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
138             $offset += 2;
139
140             $ThisFileInfo['video']['resolution_x']    = $thisfile_bmp_header_raw['width'];
141             $ThisFileInfo['video']['resolution_y']    = $thisfile_bmp_header_raw['height'];
142             $ThisFileInfo['video']['codec']           = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
143             $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
144
145             if ($thisfile_bmp['type_version'] >= 2) {
146                 // DWORD  Compression;      /* Bitmap compression scheme */
147                 // DWORD  ImageDataSize;    /* Size of bitmap data in bytes */
148                 // DWORD  XResolution;      /* X resolution of display device */
149                 // DWORD  YResolution;      /* Y resolution of display device */
150                 // DWORD  ColorsUsed;       /* Number of color table indices used */
151                 // DWORD  ColorsImportant;  /* Number of important color indices */
152                 // WORD   Units;            /* Type of units used to measure resolution */
153                 // WORD   Reserved;         /* Pad structure to 4-byte boundary */
154                 // WORD   Recording;        /* Recording algorithm */
155                 // WORD   Rendering;        /* Halftoning algorithm used */
156                 // DWORD  Size1;            /* Reserved for halftoning algorithm use */
157                 // DWORD  Size2;            /* Reserved for halftoning algorithm use */
158                 // DWORD  ColorEncoding;    /* Color model used in bitmap */
159                 // DWORD  Identifier;       /* Reserved for application use */
160
161                 $thisfile_bmp_header_raw['compression']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
162                 $offset += 4;
163                 $thisfile_bmp_header_raw['bmp_data_size']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
164                 $offset += 4;
165                 $thisfile_bmp_header_raw['resolution_h']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
166                 $offset += 4;
167                 $thisfile_bmp_header_raw['resolution_v']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
168                 $offset += 4;
169                 $thisfile_bmp_header_raw['colors_used']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
170                 $offset += 4;
171                 $thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
172                 $offset += 4;
173                 $thisfile_bmp_header_raw['resolution_units'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
174                 $offset += 2;
175                 $thisfile_bmp_header_raw['reserved1']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
176                 $offset += 2;
177                 $thisfile_bmp_header_raw['recording']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
178                 $offset += 2;
179                 $thisfile_bmp_header_raw['rendering']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
180                 $offset += 2;
181                 $thisfile_bmp_header_raw['size1']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
182                 $offset += 4;
183                 $thisfile_bmp_header_raw['size2']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
184                 $offset += 4;
185                 $thisfile_bmp_header_raw['color_encoding']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
186                 $offset += 4;
187                 $thisfile_bmp_header_raw['identifier']       = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
188                 $offset += 4;
189
190                 $thisfile_bmp_header['compression']          = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
191
192                 $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
193             }
194
195         } elseif ($thisfile_bmp['type_os'] == 'Windows') {
196
197             // Windows-format BMP
198
199             // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
200             // all versions
201             // DWORD  biSize;
202             // LONG   biWidth;
203             // LONG   biHeight;
204             // WORD   biPlanes;
205             // WORD   biBitCount;
206             // DWORD  biCompression;
207             // DWORD  biSizeImage;
208             // LONG   biXPelsPerMeter;
209             // LONG   biYPelsPerMeter;
210             // DWORD  biClrUsed;
211             // DWORD  biClrImportant;
212
213             $thisfile_bmp_header_raw['width']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
214             $offset += 4;
215             $thisfile_bmp_header_raw['height']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
216             $offset += 4;
217             $thisfile_bmp_header_raw['planes']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
218             $offset += 2;
219             $thisfile_bmp_header_raw['bits_per_pixel']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
220             $offset += 2;
221             $thisfile_bmp_header_raw['compression']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
222             $offset += 4;
223             $thisfile_bmp_header_raw['bmp_data_size']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
224             $offset += 4;
225             $thisfile_bmp_header_raw['resolution_h']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
226             $offset += 4;
227             $thisfile_bmp_header_raw['resolution_v']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
228             $offset += 4;
229             $thisfile_bmp_header_raw['colors_used']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
230             $offset += 4;
231             $thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
232             $offset += 4;
233
234             $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
235             $ThisFileInfo['video']['resolution_x']    = $thisfile_bmp_header_raw['width'];
236             $ThisFileInfo['video']['resolution_y']    = $thisfile_bmp_header_raw['height'];
237             $ThisFileInfo['video']['codec']           = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
238             $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
239
240             if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
241                 // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
242                 $BMPheader .= substr($BMPdata, $overalloffset, 44);
243                 $overalloffset += 44;
244
245                 // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
246                 // Win95+, WinNT4.0+
247                 // DWORD        bV4RedMask;
248                 // DWORD        bV4GreenMask;
249                 // DWORD        bV4BlueMask;
250                 // DWORD        bV4AlphaMask;
251                 // DWORD        bV4CSType;
252                 // CIEXYZTRIPLE bV4Endpoints;
253                 // DWORD        bV4GammaRed;
254                 // DWORD        bV4GammaGreen;
255                 // DWORD        bV4GammaBlue;
256                 $thisfile_bmp_header_raw['red_mask']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
257                 $offset += 4;
258                 $thisfile_bmp_header_raw['green_mask']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
259                 $offset += 4;
260                 $thisfile_bmp_header_raw['blue_mask']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
261                 $offset += 4;
262                 $thisfile_bmp_header_raw['alpha_mask']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
263                 $offset += 4;
264                 $thisfile_bmp_header_raw['cs_type']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
265                 $offset += 4;
266                 $thisfile_bmp_header_raw['ciexyz_red']   =                         substr($BMPheader, $offset, 4);
267                 $offset += 4;
268                 $thisfile_bmp_header_raw['ciexyz_green'] =                         substr($BMPheader, $offset, 4);
269                 $offset += 4;
270                 $thisfile_bmp_header_raw['ciexyz_blue']  =                         substr($BMPheader, $offset, 4);
271                 $offset += 4;
272                 $thisfile_bmp_header_raw['gamma_red']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
273                 $offset += 4;
274                 $thisfile_bmp_header_raw['gamma_green']  = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
275                 $offset += 4;
276                 $thisfile_bmp_header_raw['gamma_blue']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
277                 $offset += 4;
278
279                 $thisfile_bmp_header['ciexyz_red']   = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
280                 $thisfile_bmp_header['ciexyz_green'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
281                 $thisfile_bmp_header['ciexyz_blue']  = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
282             }
283
284             if ($thisfile_bmp['type_version'] >= 5) {
285                 $BMPheader .= substr($BMPdata, $overalloffset, 16);
286                 $overalloffset += 16;
287
288                 // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
289                 // Win98+, Win2000+
290                 // DWORD        bV5Intent;
291                 // DWORD        bV5ProfileData;
292                 // DWORD        bV5ProfileSize;
293                 // DWORD        bV5Reserved;
294                 $thisfile_bmp_header_raw['intent']              = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
295                 $offset += 4;
296                 $thisfile_bmp_header_raw['profile_data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
297                 $offset += 4;
298                 $thisfile_bmp_header_raw['profile_data_size']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
299                 $offset += 4;
300                 $thisfile_bmp_header_raw['reserved3']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
301                 $offset += 4;
302             }
303
304         } else {
305
306             $ThisFileInfo['error'][] = 'Unknown BMP format in header.';
307             return false;
308
309         }
310
311         if ($ExtractPalette || $ExtractData) {
312             $PaletteEntries = 0;
313             if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
314                 $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
315             } elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
316                 $PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
317             }
318             if ($PaletteEntries > 0) {
319                 $BMPpalette = substr($BMPdata, $overalloffset, 4 * $PaletteEntries);
320                 $overalloffset += 4 * $PaletteEntries;
321
322                 $paletteoffset = 0;
323                 for ($i = 0; $i < $PaletteEntries; $i++) {
324                     // RGBQUAD          - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
325                     // BYTE    rgbBlue;
326                     // BYTE    rgbGreen;
327                     // BYTE    rgbRed;
328                     // BYTE    rgbReserved;
329                     $blue  = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
330                     $green = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
331                     $red   = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
332                     if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
333                         // no padding byte
334                     } else {
335                         $paletteoffset++; // padding byte
336                     }
337                     $thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | ($blue));
338                 }
339             }
340         }
341
342         if ($ExtractData) {
343             $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
344
345             $BMPpixelData = substr($BMPdata, $thisfile_bmp_header_raw['data_offset'], $thisfile_bmp_header_raw['height'] * $RowByteLength);
346             $overalloffset = $thisfile_bmp_header_raw['data_offset'] + ($thisfile_bmp_header_raw['height'] * $RowByteLength);
347
348             $pixeldataoffset = 0;
349             switch (@$thisfile_bmp_header_raw['compression']) {
350