PageRenderTime 33ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/Vendor/phpThumb/phpthumb.bmp.php

https://bitbucket.org/aaoliveira/cakephp-phpthumb
PHP | 878 lines | 632 code | 106 blank | 140 comment | 127 complexity | 7c46ac47155ae8518338066f17c2972f MD5 | raw file
  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. class phpthumb_bmp {
  20. function phpthumb_bmp() {
  21. return true;
  22. }
  23. function phpthumb_bmp2gd(&$BMPdata, $truecolor=true) {
  24. $ThisFileInfo = array();
  25. if ($this->getid3_bmp($BMPdata, $ThisFileInfo, true, true)) {
  26. $gd = $this->PlotPixelsGD($ThisFileInfo['bmp'], $truecolor);
  27. return $gd;
  28. }
  29. return false;
  30. }
  31. function phpthumb_bmpfile2gd($filename, $truecolor=true) {
  32. if ($fp = @fopen($filename, 'rb')) {
  33. $BMPdata = fread($fp, filesize($filename));
  34. fclose($fp);
  35. return $this->phpthumb_bmp2gd($BMPdata, $truecolor);
  36. }
  37. return false;
  38. }
  39. function GD2BMPstring(&$gd_image) {
  40. $imageX = ImageSX($gd_image);
  41. $imageY = ImageSY($gd_image);
  42. $BMP = '';
  43. for ($y = ($imageY - 1); $y >= 0; $y--) {
  44. $thisline = '';
  45. for ($x = 0; $x < $imageX; $x++) {
  46. $argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y);
  47. $thisline .= chr($argb['blue']).chr($argb['green']).chr($argb['red']);
  48. }
  49. while (strlen($thisline) % 4) {
  50. $thisline .= "\x00";
  51. }
  52. $BMP .= $thisline;
  53. }
  54. $bmpSize = strlen($BMP) + 14 + 40;
  55. // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
  56. $BITMAPFILEHEADER = 'BM'; // WORD bfType;
  57. $BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String($bmpSize, 4); // DWORD bfSize;
  58. $BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String( 0, 2); // WORD bfReserved1;
  59. $BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String( 0, 2); // WORD bfReserved2;
  60. $BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String( 54, 4); // DWORD bfOffBits;
  61. // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
  62. $BITMAPINFOHEADER = phpthumb_functions::LittleEndian2String( 40, 4); // DWORD biSize;
  63. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( $imageX, 4); // LONG biWidth;
  64. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( $imageY, 4); // LONG biHeight;
  65. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 1, 2); // WORD biPlanes;
  66. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 24, 2); // WORD biBitCount;
  67. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 0, 4); // DWORD biCompression;
  68. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 0, 4); // DWORD biSizeImage;
  69. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 2835, 4); // LONG biXPelsPerMeter;
  70. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 2835, 4); // LONG biYPelsPerMeter;
  71. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 0, 4); // DWORD biClrUsed;
  72. $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 0, 4); // DWORD biClrImportant;
  73. return $BITMAPFILEHEADER.$BITMAPINFOHEADER.$BMP;
  74. }
  75. function getid3_bmp(&$BMPdata, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) {
  76. // shortcuts
  77. $ThisFileInfo['bmp']['header']['raw'] = array();
  78. $thisfile_bmp = &$ThisFileInfo['bmp'];
  79. $thisfile_bmp_header = &$thisfile_bmp['header'];
  80. $thisfile_bmp_header_raw = &$thisfile_bmp_header['raw'];
  81. // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
  82. // all versions
  83. // WORD bfType;
  84. // DWORD bfSize;
  85. // WORD bfReserved1;
  86. // WORD bfReserved2;
  87. // DWORD bfOffBits;
  88. $offset = 0;
  89. $overalloffset = 0;
  90. $BMPheader = substr($BMPdata, $overalloffset, 14 + 40);
  91. $overalloffset += (14 + 40);
  92. $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
  93. $offset += 2;
  94. if ($thisfile_bmp_header_raw['identifier'] != 'BM') {
  95. $ThisFileInfo['error'][] = 'Expecting "BM" at offset '.intval(@$ThisFileInfo['avdataoffset']).', found "'.$thisfile_bmp_header_raw['identifier'].'"';
  96. unset($ThisFileInfo['fileformat']);
  97. unset($ThisFileInfo['bmp']);
  98. return false;
  99. }
  100. $thisfile_bmp_header_raw['filesize'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  101. $offset += 4;
  102. $thisfile_bmp_header_raw['reserved1'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  103. $offset += 2;
  104. $thisfile_bmp_header_raw['reserved2'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  105. $offset += 2;
  106. $thisfile_bmp_header_raw['data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  107. $offset += 4;
  108. $thisfile_bmp_header_raw['header_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  109. $offset += 4;
  110. // check if the hardcoded-to-1 "planes" is at offset 22 or 26
  111. $planes22 = $this->LittleEndian2Int(substr($BMPheader, 22, 2));
  112. $planes26 = $this->LittleEndian2Int(substr($BMPheader, 26, 2));
  113. if (($planes22 == 1) && ($planes26 != 1)) {
  114. $thisfile_bmp['type_os'] = 'OS/2';
  115. $thisfile_bmp['type_version'] = 1;
  116. } elseif (($planes26 == 1) && ($planes22 != 1)) {
  117. $thisfile_bmp['type_os'] = 'Windows';
  118. $thisfile_bmp['type_version'] = 1;
  119. } elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
  120. $thisfile_bmp['type_os'] = 'OS/2';
  121. $thisfile_bmp['type_version'] = 1;
  122. } elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
  123. $thisfile_bmp['type_os'] = 'Windows';
  124. $thisfile_bmp['type_version'] = 1;
  125. } elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
  126. $thisfile_bmp['type_os'] = 'Windows';
  127. $thisfile_bmp['type_version'] = 4;
  128. } elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
  129. $thisfile_bmp['type_os'] = 'Windows';
  130. $thisfile_bmp['type_version'] = 5;
  131. } else {
  132. $ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)';
  133. unset($ThisFileInfo['fileformat']);
  134. unset($ThisFileInfo['bmp']);
  135. return false;
  136. }
  137. $ThisFileInfo['fileformat'] = 'bmp';
  138. $ThisFileInfo['video']['dataformat'] = 'bmp';
  139. $ThisFileInfo['video']['lossless'] = true;
  140. $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
  141. if ($thisfile_bmp['type_os'] == 'OS/2') {
  142. // OS/2-format BMP
  143. // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
  144. // DWORD Size; /* Size of this structure in bytes */
  145. // DWORD Width; /* Bitmap width in pixels */
  146. // DWORD Height; /* Bitmap height in pixel */
  147. // WORD NumPlanes; /* Number of bit planes (color depth) */
  148. // WORD BitsPerPixel; /* Number of bits per pixel per plane */
  149. $thisfile_bmp_header_raw['width'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  150. $offset += 2;
  151. $thisfile_bmp_header_raw['height'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  152. $offset += 2;
  153. $thisfile_bmp_header_raw['planes'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  154. $offset += 2;
  155. $thisfile_bmp_header_raw['bits_per_pixel'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  156. $offset += 2;
  157. $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
  158. $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
  159. $ThisFileInfo['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
  160. $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
  161. if ($thisfile_bmp['type_version'] >= 2) {
  162. // DWORD Compression; /* Bitmap compression scheme */
  163. // DWORD ImageDataSize; /* Size of bitmap data in bytes */
  164. // DWORD XResolution; /* X resolution of display device */
  165. // DWORD YResolution; /* Y resolution of display device */
  166. // DWORD ColorsUsed; /* Number of color table indices used */
  167. // DWORD ColorsImportant; /* Number of important color indices */
  168. // WORD Units; /* Type of units used to measure resolution */
  169. // WORD Reserved; /* Pad structure to 4-byte boundary */
  170. // WORD Recording; /* Recording algorithm */
  171. // WORD Rendering; /* Halftoning algorithm used */
  172. // DWORD Size1; /* Reserved for halftoning algorithm use */
  173. // DWORD Size2; /* Reserved for halftoning algorithm use */
  174. // DWORD ColorEncoding; /* Color model used in bitmap */
  175. // DWORD Identifier; /* Reserved for application use */
  176. $thisfile_bmp_header_raw['compression'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  177. $offset += 4;
  178. $thisfile_bmp_header_raw['bmp_data_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  179. $offset += 4;
  180. $thisfile_bmp_header_raw['resolution_h'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  181. $offset += 4;
  182. $thisfile_bmp_header_raw['resolution_v'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  183. $offset += 4;
  184. $thisfile_bmp_header_raw['colors_used'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  185. $offset += 4;
  186. $thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  187. $offset += 4;
  188. $thisfile_bmp_header_raw['resolution_units'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  189. $offset += 2;
  190. $thisfile_bmp_header_raw['reserved1'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  191. $offset += 2;
  192. $thisfile_bmp_header_raw['recording'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  193. $offset += 2;
  194. $thisfile_bmp_header_raw['rendering'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  195. $offset += 2;
  196. $thisfile_bmp_header_raw['size1'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  197. $offset += 4;
  198. $thisfile_bmp_header_raw['size2'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  199. $offset += 4;
  200. $thisfile_bmp_header_raw['color_encoding'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  201. $offset += 4;
  202. $thisfile_bmp_header_raw['identifier'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  203. $offset += 4;
  204. $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
  205. $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
  206. }
  207. } elseif ($thisfile_bmp['type_os'] == 'Windows') {
  208. // Windows-format BMP
  209. // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
  210. // all versions
  211. // DWORD biSize;
  212. // LONG biWidth;
  213. // LONG biHeight;
  214. // WORD biPlanes;
  215. // WORD biBitCount;
  216. // DWORD biCompression;
  217. // DWORD biSizeImage;
  218. // LONG biXPelsPerMeter;
  219. // LONG biYPelsPerMeter;
  220. // DWORD biClrUsed;
  221. // DWORD biClrImportant;
  222. $thisfile_bmp_header_raw['width'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
  223. $offset += 4;
  224. $thisfile_bmp_header_raw['height'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
  225. $offset += 4;
  226. $thisfile_bmp_header_raw['planes'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  227. $offset += 2;
  228. $thisfile_bmp_header_raw['bits_per_pixel'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
  229. $offset += 2;
  230. $thisfile_bmp_header_raw['compression'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  231. $offset += 4;
  232. $thisfile_bmp_header_raw['bmp_data_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  233. $offset += 4;
  234. $thisfile_bmp_header_raw['resolution_h'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
  235. $offset += 4;
  236. $thisfile_bmp_header_raw['resolution_v'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
  237. $offset += 4;
  238. $thisfile_bmp_header_raw['colors_used'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  239. $offset += 4;
  240. $thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  241. $offset += 4;
  242. $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
  243. $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
  244. $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
  245. $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
  246. $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
  247. if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
  248. // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
  249. $BMPheader .= substr($BMPdata, $overalloffset, 44);
  250. $overalloffset += 44;
  251. // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
  252. // Win95+, WinNT4.0+
  253. // DWORD bV4RedMask;
  254. // DWORD bV4GreenMask;
  255. // DWORD bV4BlueMask;
  256. // DWORD bV4AlphaMask;
  257. // DWORD bV4CSType;
  258. // CIEXYZTRIPLE bV4Endpoints;
  259. // DWORD bV4GammaRed;
  260. // DWORD bV4GammaGreen;
  261. // DWORD bV4GammaBlue;
  262. $thisfile_bmp_header_raw['red_mask'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  263. $offset += 4;
  264. $thisfile_bmp_header_raw['green_mask'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  265. $offset += 4;
  266. $thisfile_bmp_header_raw['blue_mask'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  267. $offset += 4;
  268. $thisfile_bmp_header_raw['alpha_mask'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  269. $offset += 4;
  270. $thisfile_bmp_header_raw['cs_type'] = $this->LittleEndian2Int(substr($BMPheader, $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;
  278. $thisfile_bmp_header_raw['gamma_red'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  279. $offset += 4;
  280. $thisfile_bmp_header_raw['gamma_green'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  281. $offset += 4;
  282. $thisfile_bmp_header_raw['gamma_blue'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  283. $offset += 4;
  284. $thisfile_bmp_header['ciexyz_red'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
  285. $thisfile_bmp_header['ciexyz_green'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
  286. $thisfile_bmp_header['ciexyz_blue'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
  287. }
  288. if ($thisfile_bmp['type_version'] >= 5) {
  289. $BMPheader .= substr($BMPdata, $overalloffset, 16);
  290. $overalloffset += 16;
  291. // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
  292. // Win98+, Win2000+
  293. // DWORD bV5Intent;
  294. // DWORD bV5ProfileData;
  295. // DWORD bV5ProfileSize;
  296. // DWORD bV5Reserved;
  297. $thisfile_bmp_header_raw['intent'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  298. $offset += 4;
  299. $thisfile_bmp_header_raw['profile_data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  300. $offset += 4;
  301. $thisfile_bmp_header_raw['profile_data_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  302. $offset += 4;
  303. $thisfile_bmp_header_raw['reserved3'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
  304. $offset += 4;
  305. }
  306. } else {
  307. $ThisFileInfo['error'][] = 'Unknown BMP format in header.';
  308. return false;
  309. }
  310. if ($ExtractPalette || $ExtractData) {
  311. $PaletteEntries = 0;
  312. if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
  313. $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
  314. } elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
  315. $PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
  316. }
  317. if ($PaletteEntries > 0) {
  318. $BMPpalette = substr($BMPdata, $overalloffset, 4 * $PaletteEntries);
  319. $overalloffset += 4 * $PaletteEntries;
  320. $paletteoffset = 0;
  321. for ($i = 0; $i < $PaletteEntries; $i++) {
  322. // RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
  323. // BYTE rgbBlue;
  324. // BYTE rgbGreen;
  325. // BYTE rgbRed;
  326. // BYTE rgbReserved;
  327. $blue = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
  328. $green = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
  329. $red = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
  330. if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
  331. // no padding byte
  332. } else {
  333. $paletteoffset++; // padding byte
  334. }
  335. $thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | ($blue));
  336. }
  337. }
  338. }
  339. if ($ExtractData) {
  340. $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
  341. $BMPpixelData = substr($BMPdata, $thisfile_bmp_header_raw['data_offset'], $thisfile_bmp_header_raw['height'] * $RowByteLength);
  342. $overalloffset = $thisfile_bmp_header_raw['data_offset'] + ($thisfile_bmp_header_raw['height'] * $RowByteLength);
  343. $pixeldataoffset = 0;
  344. switch (@$thisfile_bmp_header_raw['compression']) {
  345. case 0: // BI_RGB
  346. switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
  347. case 1:
  348. for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
  349. for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
  350. $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
  351. for ($i = 7; $i >= 0; $i--) {
  352. $paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
  353. $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
  354. $col++;
  355. }
  356. }
  357. while (($pixeldataoffset % 4) != 0) {
  358. // lines are padded to nearest DWORD
  359. $pixeldataoffset++;
  360. }
  361. }
  362. break;
  363. case 4:
  364. for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
  365. for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
  366. $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
  367. for ($i = 1; $i >= 0; $i--) {
  368. $paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
  369. $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
  370. $col++;
  371. }
  372. }
  373. while (($pixeldataoffset % 4) != 0) {
  374. // lines are padded to nearest DWORD
  375. $pixeldataoffset++;
  376. }
  377. }
  378. break;
  379. case 8:
  380. for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
  381. for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
  382. $paletteindex = ord($BMPpixelData{$pixeldataoffset++});
  383. $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
  384. }
  385. while (($pixeldataoffset % 4) != 0) {
  386. // lines are padded to nearest DWORD
  387. $pixeldataoffset++;
  388. }
  389. }
  390. break;
  391. case 24:
  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+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
  395. $pixeldataoffset += 3;
  396. }
  397. while (($pixeldataoffset % 4) != 0) {
  398. // lines are padded to nearest DWORD
  399. $pixeldataoffset++;
  400. }
  401. }
  402. break;
  403. case 32:
  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+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
  407. $pixeldataoffset += 4;
  408. }
  409. while (($pixeldataoffset % 4) != 0) {
  410. // lines are padded to nearest DWORD
  411. $pixeldataoffset++;
  412. }
  413. }
  414. break;
  415. case 16:
  416. // ?
  417. break;
  418. default:
  419. $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
  420. break;
  421. }
  422. break;
  423. case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
  424. switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
  425. case 8:
  426. $pixelcounter = 0;
  427. while ($pixeldataoffset < strlen($BMPpixelData)) {
  428. $firstbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  429. $secondbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  430. if ($firstbyte == 0) {
  431. // escaped/absolute mode - the first byte of the pair can be set to zero to
  432. // indicate an escape character that denotes the end of a line, the end of
  433. // a bitmap, or a delta, depending on the value of the second byte.
  434. switch ($secondbyte) {
  435. case 0:
  436. // end of line
  437. // no need for special processing, just ignore
  438. break;
  439. case 1:
  440. // end of bitmap
  441. $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
  442. break;
  443. case 2:
  444. // delta - The 2 bytes following the escape contain unsigned values
  445. // indicating the horizontal and vertical offsets of the next pixel
  446. // from the current position.
  447. $colincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  448. $rowincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  449. $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
  450. $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
  451. $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
  452. break;
  453. default:
  454. // In absolute mode, the first byte is zero and the second byte is a
  455. // value in the range 03H through FFH. The second byte represents the
  456. // number of bytes that follow, each of which contains the color index
  457. // of a single pixel. Each run must be aligned on a word boundary.
  458. for ($i = 0; $i < $secondbyte; $i++) {
  459. $paletteindex = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  460. $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
  461. $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
  462. $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
  463. $pixelcounter++;
  464. }
  465. while (($pixeldataoffset % 2) != 0) {
  466. // Each run must be aligned on a word boundary.
  467. $pixeldataoffset++;
  468. }
  469. break;
  470. }
  471. } else {
  472. // encoded mode - the first byte specifies the number of consecutive pixels
  473. // to be drawn using the color index contained in the second byte.
  474. for ($i = 0; $i < $firstbyte; $i++) {
  475. $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
  476. $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
  477. $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
  478. $pixelcounter++;
  479. }
  480. }
  481. }
  482. break;
  483. default:
  484. $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
  485. break;
  486. }
  487. break;
  488. case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
  489. switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
  490. case 4:
  491. $pixelcounter = 0;
  492. while ($pixeldataoffset < strlen($BMPpixelData)) {
  493. $firstbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  494. $secondbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  495. if ($firstbyte == 0) {
  496. // escaped/absolute mode - the first byte of the pair can be set to zero to
  497. // indicate an escape character that denotes the end of a line, the end of
  498. // a bitmap, or a delta, depending on the value of the second byte.
  499. switch ($secondbyte) {
  500. case 0:
  501. // end of line
  502. // no need for special processing, just ignore
  503. break;
  504. case 1:
  505. // end of bitmap
  506. $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
  507. break;
  508. case 2:
  509. // delta - The 2 bytes following the escape contain unsigned values
  510. // indicating the horizontal and vertical offsets of the next pixel
  511. // from the current position.
  512. $colincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  513. $rowincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  514. $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
  515. $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
  516. $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
  517. break;
  518. default:
  519. // In absolute mode, the first byte is zero. The second byte contains the number
  520. // of color indexes that follow. Subsequent bytes contain color indexes in their
  521. // high- and low-order 4 bits, one color index for each pixel. In absolute mode,
  522. // each run must be aligned on a word boundary.
  523. unset($paletteindexes);
  524. for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
  525. $paletteindexbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
  526. $paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
  527. $paletteindexes[] = ($paletteindexbyte & 0x0F);
  528. }
  529. while (($pixeldataoffset % 2) != 0) {
  530. // Each run must be aligned on a word boundary.
  531. $pixeldataoffset++;
  532. }
  533. foreach ($paletteindexes as $dummy => $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. } else {
  542. // encoded mode - the first byte of the pair contains the number of pixels to be
  543. // drawn using the color indexes in the second byte. The second byte contains two
  544. // color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
  545. // The first of the pixels is drawn using the color specified by the high-order
  546. // 4 bits, the second is drawn using the color in the low-order 4 bits, the third
  547. // is drawn using the color in the high-order 4 bits, and so on, until all the
  548. // pixels specified by the first byte have been drawn.
  549. $paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
  550. $paletteindexes[1] = ($secondbyte & 0x0F);
  551. for ($i = 0; $i < $firstbyte; $i++) {
  552. $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
  553. $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
  554. $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]];
  555. $pixelcounter++;
  556. }
  557. }
  558. }
  559. break;
  560. default:
  561. $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
  562. break;
  563. }
  564. break;
  565. case 3: // BI_BITFIELDS
  566. switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
  567. case 16:
  568. case 32:
  569. $redshift = 0;
  570. $greenshift = 0;
  571. $blueshift = 0;
  572. if (!$thisfile_bmp_header_raw['red_mask'] || !$thisfile_bmp_header_raw['green_mask'] || !$thisfile_bmp_header_raw['blue_mask']) {
  573. $ThisFileInfo['error'][] = 'missing $thisfile_bmp_header_raw[(red|green|blue)_mask]';
  574. return false;
  575. }
  576. while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
  577. $redshift++;
  578. }
  579. while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
  580. $greenshift++;
  581. }
  582. while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
  583. $blueshift++;
  584. }
  585. for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
  586. for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
  587. $pixelvalue = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
  588. $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
  589. $red = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift)) * 255));
  590. $green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255));
  591. $blue = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift)) * 255));
  592. $thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue));
  593. }
  594. while (($pixeldataoffset % 4) != 0) {
  595. // lines are padded to nearest DWORD
  596. $pixeldataoffset++;
  597. }
  598. }
  599. break;
  600. default:
  601. $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
  602. break;
  603. }
  604. break;
  605. default: // unhandled compression type
  606. $ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data';
  607. break;
  608. }
  609. }
  610. return true;
  611. }
  612. function IntColor2RGB($color) {
  613. $red = ($color & 0x00FF0000) >> 16;
  614. $green = ($color & 0x0000FF00) >> 8;
  615. $blue = ($color & 0x000000FF);
  616. return array($red, $green, $blue);
  617. }
  618. function PlotPixelsGD(&$BMPdata, $truecolor=true) {
  619. $imagewidth = $BMPdata['header']['raw']['width'];
  620. $imageheight = $BMPdata['header']['raw']['height'];
  621. if ($truecolor) {
  622. $gd = @ImageCreateTrueColor($imagewidth, $imageheight);
  623. } else {
  624. $gd = @ImageCreate($imagewidth, $imageheight);
  625. if (!empty($BMPdata['palette'])) {
  626. // create GD palette from BMP palette
  627. foreach ($BMPdata['palette'] as $dummy => $color) {
  628. list($r, $g, $b) = $this->IntColor2RGB($color);
  629. ImageColorAllocate($gd, $r, $g, $b);
  630. }
  631. } else {
  632. // create 216-color websafe palette
  633. for ($r = 0x00; $r <= 0xFF; $r += 0x33) {
  634. for ($g = 0x00; $g <= 0xFF; $g += 0x33) {
  635. for ($b = 0x00; $b <= 0xFF; $b += 0x33) {
  636. ImageColorAllocate($gd, $r, $g, $b);
  637. }
  638. }
  639. }
  640. }
  641. }
  642. if (!is_resource($gd)) {
  643. return false;
  644. }
  645. foreach ($BMPdata['data'] as $row => $colarray) {
  646. if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) {
  647. set_time_limit(30);
  648. }
  649. foreach ($colarray as $col => $color) {
  650. list($red, $green, $blue) = $this->IntColor2RGB($color);
  651. if ($truecolor) {
  652. $pixelcolor = ImageColorAllocate($gd, $red, $green, $blue);
  653. } else {
  654. $pixelcolor = ImageColorClosest($gd, $red, $green, $blue);
  655. }
  656. ImageSetPixel($gd, $col, $row, $pixelcolor);
  657. }
  658. }
  659. return $gd;
  660. }
  661. function PlotBMP(&$BMPinfo) {
  662. $starttime = time();
  663. if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) {
  664. echo 'ERROR: no pixel data<BR>';
  665. return false;
  666. }
  667. if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) {
  668. set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000)));
  669. }
  670. $im = $this->PlotPixelsGD($BMPinfo['bmp']);
  671. if (headers_sent()) {
  672. echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds<BR>';
  673. ImageDestroy($im);
  674. exit;
  675. } else {
  676. header('Content-Type: image/png');
  677. ImagePNG($im);
  678. ImageDestroy($im);
  679. return true;
  680. }
  681. return false;
  682. }
  683. function BMPcompressionWindowsLookup($compressionid) {
  684. static $BMPcompressionWindowsLookup = array(
  685. 0 => 'BI_RGB',
  686. 1 => 'BI_RLE8',
  687. 2 => 'BI_RLE4',
  688. 3 => 'BI_BITFIELDS',
  689. 4 => 'BI_JPEG',
  690. 5 => 'BI_PNG'
  691. );
  692. return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid');
  693. }
  694. function BMPcompressionOS2Lookup($compressionid) {
  695. static $BMPcompressionOS2Lookup = array(
  696. 0 => 'BI_RGB',
  697. 1 => 'BI_RLE8',
  698. 2 => 'BI_RLE4',
  699. 3 => 'Huffman 1D',
  700. 4 => 'BI_RLE24',
  701. );
  702. return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid');
  703. }
  704. // from getid3.lib.php
  705. function trunc($floatnumber) {
  706. // truncates a floating-point number at the decimal point
  707. // returns int (if possible, otherwise float)
  708. if ($floatnumber >= 1) {
  709. $truncatednumber = floor($floatnumber);
  710. } elseif ($floatnumber <= -1) {
  711. $truncatednumber = ceil($floatnumber);
  712. } else {
  713. $truncatednumber = 0;
  714. }
  715. if ($truncatednumber <= 1073741824) { // 2^30
  716. $truncatednumber = (int) $truncatednumber;
  717. }
  718. return $truncatednumber;
  719. }
  720. function LittleEndian2Int($byteword) {
  721. $intvalue = 0;
  722. $byteword = strrev($byteword);
  723. $bytewordlen = strlen($byteword);
  724. for ($i = 0; $i < $bytewordlen; $i++) {
  725. $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
  726. }
  727. return $intvalue;
  728. }
  729. function BigEndian2Int($byteword) {
  730. return $this->LittleEndian2Int(strrev($byteword));
  731. }
  732. function BigEndian2Bin($byteword) {
  733. $binvalue = '';
  734. $bytewordlen = strlen($byteword);
  735. for ($i = 0; $i < $bytewordlen; $i++) {
  736. $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
  737. }
  738. return $binvalue;
  739. }
  740. function FixedPoint2_30($rawdata) {
  741. $binarystring = $this->BigEndian2Bin($rawdata);
  742. return $this->Bin2Dec(substr($binarystring, 0, 2)) + (float) ($this->Bin2Dec(substr($binarystring, 2, 30)) / 1073741824);
  743. }
  744. function Bin2Dec($binstring, $signed=false) {
  745. $signmult = 1;
  746. if ($signed) {
  747. if ($binstring{0} == '1') {
  748. $signmult = -1;
  749. }
  750. $binstring = substr($binstring, 1);
  751. }
  752. $decvalue = 0;
  753. for ($i = 0; $i < strlen($binstring); $i++) {
  754. $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
  755. }
  756. return $this->CastAsInt($decvalue * $signmult);
  757. }
  758. function CastAsInt($floatnum) {
  759. // convert to float if not already
  760. $floatnum = (float) $floatnum;
  761. // convert a float to type int, only if possible
  762. if ($this->trunc($floatnum) == $floatnum) {
  763. // it's not floating point
  764. if ($floatnum <= 1073741824) { // 2^30
  765. // it's within int range
  766. $floatnum = (int) $floatnum;
  767. }
  768. }
  769. return $floatnum;
  770. }
  771. }
  772. ?>