1: <?php
2:
3: /**
4: ##DOC-SIGNATURE##
5:
6: This file is part of WideImage.
7:
8: WideImage is free software; you can redistribute it and/or modify
9: it under the terms of the GNU Lesser General Public License as published by
10: the Free Software Foundation; either version 2.1 of the License, or
11: (at your option) any later version.
12:
13: WideImage is distributed in the hope that it will be useful,
14: but WITHOUT ANY WARRANTY; without even the implied warranty of
15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: GNU Lesser General Public License for more details.
17:
18: You should have received a copy of the GNU Lesser General Public License
19: along with WideImage; if not, write to the Free Software
20: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21:
22: * @package Internal/Mappers
23: **/
24:
25: /**
26: * External code for BMP
27: *
28: * Adapted for use in WideImage. Code used with permission from the original author de77.
29: * http://de77.com/php/read-and-write-bmp-in-php-imagecreatefrombmp-imagebmp
30: *
31: * @author de77
32: * @license MIT
33: * @url de77.com
34: * @version 21.08.2010
35: *
36: * @package Internal/Mappers
37: */
38:
39: namespace WideImage\vendor\de77;
40:
41: class BMP
42: {
43: public static function imagebmp(&$img, $filename = false)
44: {
45: $wid = imagesx($img);
46: $hei = imagesy($img);
47: $wid_pad = str_pad('', $wid % 4, "\0");
48:
49: $size = 54 + ($wid + $wid_pad) * $hei * 3; //fixed
50:
51: //prepare & save header
52: $header['identifier'] = 'BM';
53: $header['file_size'] = self::dword($size);
54: $header['reserved'] = self::dword(0);
55: $header['bitmap_data'] = self::dword(54);
56: $header['header_size'] = self::dword(40);
57: $header['width'] = self::dword($wid);
58: $header['height'] = self::dword($hei);
59: $header['planes'] = self::word(1);
60: $header['bits_per_pixel'] = self::word(24);
61: $header['compression'] = self::dword(0);
62: $header['data_size'] = self::dword(0);
63: $header['h_resolution'] = self::dword(0);
64: $header['v_resolution'] = self::dword(0);
65: $header['colors'] = self::dword(0);
66: $header['important_colors'] = self::dword(0);
67:
68: if ($filename) {
69: $f = fopen($filename, "wb");
70:
71: foreach ($header as $h) {
72: fwrite($f, $h);
73: }
74:
75: //save pixels
76: for ($y = $hei-1; $y >= 0; $y--) {
77: for ($x = 0; $x < $wid; $x++) {
78: $rgb = imagecolorat($img, $x, $y);
79: fwrite($f, self::byte3($rgb));
80: }
81:
82: fwrite($f, $wid_pad);
83: }
84:
85: fclose($f);
86: } else {
87: foreach ($header as $h) {
88: echo $h;
89: }
90:
91: //save pixels
92: for ($y = $hei-1; $y >= 0; $y--) {
93: for ($x = 0; $x < $wid; $x++) {
94: $rgb = imagecolorat($img, $x, $y);
95: echo self::byte3($rgb);
96: }
97:
98: echo $wid_pad;
99: }
100: }
101:
102: return true;
103: }
104:
105: public static function imagecreatefromstring($data)
106: {
107: //read header
108: $pos = 0;
109: $header = substr($data, 0, 54);
110: $pos = 54;
111:
112: if (strlen($header) < 54) {
113: return false;
114: }
115:
116: $header = unpack( 'c2identifier/Vfile_size/Vreserved/Vbitmap_data/Vheader_size/' .
117: 'Vwidth/Vheight/vplanes/vbits_per_pixel/Vcompression/Vdata_size/'.
118: 'Vh_resolution/Vv_resolution/Vcolors/Vimportant_colors', $header);
119:
120: if ($header['identifier1'] != 66 or $header['identifier2'] != 77) {
121: return false;
122: }
123:
124: if (!in_array($header['bits_per_pixel'], array(24, 32, 8, 4, 1))) {
125: return false;
126: }
127:
128: $bps = $header['bits_per_pixel']; //bits per pixel
129: $wid2 = ceil(($bps/8 * $header['width']) / 4) * 4;
130: $colors = $header['colors'];
131: // The absolute height value is necessary because ImageHeight can be negative
132: // If Height is a positive number, then the image is a "bottom-up" bitmap with the origin in the lower-left corner.
133: // If Height is a negative number, then the image is a "top-down" bitmap with the origin in the upper-left corner.
134: $header['height'] = abs($header['height']);
135:
136: $wid = $header['width'];
137: $hei = $header['height'];
138:
139: $img = imagecreatetruecolor($header['width'], $header['height']);
140: if (!$img) {
141: return false; // Invalid width or height
142: }
143: //read palette
144: $palette = [];
145:
146: if ($bps < 9) {
147: // A color table is mandatory for color depths <= 8
148: if (isset($header['colors']) && $header['colors'] > 0) {
149: // The number of entries in the palette is either 2n
150: // or a smaller number specified in the header
151: $colors = min($header['colors'], $colors);
152: }
153: for ($i = 0; $i < $colors; $i++) {
154: $palette[] = self::undword(substr($data, $pos, 4));
155: $pos += 4;
156: }
157: } else {
158: if ($bps == 32) {
159: imagealphablending($img, false);
160: imagesavealpha($img, true);
161: }
162:
163: $palette = array();
164: }
165:
166: //read pixels
167: for ($y = $hei-1; $y >= 0; $y--) {
168: $row = substr($data, $pos, $wid2);
169: $pos += $wid2;
170: $pixels = self::str_split2($row, $bps, $palette);
171:
172: for ($x = 0; $x < $wid; $x++) {
173: self::makepixel($img, $x, $y, $pixels[$x], $bps);
174: }
175: }
176:
177: return $img;
178: }
179:
180: public static function imagecreatefrombmp($filename)
181: {
182: return self::imagecreatefromstring(file_get_contents($filename));
183: }
184:
185: private static function str_split2($row, $bps, $palette)
186: {
187: switch ($bps) {
188: case 32:
189: case 24: return str_split($row, $bps / 8);
190: case 8: $out = array();
191: $count = strlen($row);
192:
193: for ($i = 0; $i < $count; $i++) {
194: $out[] = $palette[ ord($row[$i]) ];
195: }
196:
197: return $out;
198: case 4: $out = array();
199: $count = strlen($row);
200:
201: for ($i = 0; $i < $count; $i++) {
202: $roww = ord($row[$i]);
203: $out[] = $palette[ ($roww & 240) >> 4 ];
204: $out[] = $palette[ ($roww & 15) ];
205: }
206:
207: return $out;
208: case 1: $out = array();
209: $count = strlen($row);
210:
211: for ($i = 0; $i < $count; $i++) {
212: $roww = ord($row[$i]);
213: $out[] = $palette[ ($roww & 128) >> 7 ];
214: $out[] = $palette[ ($roww & 64) >> 6 ];
215: $out[] = $palette[ ($roww & 32) >> 5 ];
216: $out[] = $palette[ ($roww & 16) >> 4 ];
217: $out[] = $palette[ ($roww & 8) >> 3 ];
218: $out[] = $palette[ ($roww & 4) >> 2 ];
219: $out[] = $palette[ ($roww & 2) >> 1 ];
220: $out[] = $palette[ ($roww & 1) ];
221: }
222:
223: return $out;
224: }
225: }
226:
227: private static function makepixel($img, $x, $y, $str, $bps)
228: {
229: switch ($bps) {
230: case 32 : $a = ord($str[0]);
231: $b = ord($str[1]);
232: $c = ord($str[2]);
233: $d = 256 - ord($str[3]); //TODO: gives imperfect results
234: $pixel = $d*256*256*256 + $c*256*256 + $b*256 + $a;
235: imagesetpixel($img, $x, $y, $pixel);
236: break;
237: case 24 : $a = ord($str[0]);
238: $b = ord($str[1]);
239: $c = ord($str[2]);
240: $pixel = $c*256*256 + $b*256 + $a;
241: imagesetpixel($img, $x, $y, $pixel);
242: break;
243: case 8 :
244: case 4 :
245: case 1 : imagesetpixel($img, $x, $y, $str);
246: break;
247: }
248: }
249:
250: private static function byte3($n)
251: {
252: return chr($n & 255) . chr(($n >> 8) & 255) . chr(($n >> 16) & 255);
253: }
254:
255: private static function undword($n)
256: {
257: $r = unpack("V", $n);
258: return $r[1];
259: }
260:
261: private static function dword($n)
262: {
263: return pack("V", $n);
264: }
265:
266: private static function word($n)
267: {
268: return pack("v", $n);
269: }
270: }
271: