PluginProbe ʕ •ᴥ•ʔ
File Manager Pro – Filester / 2.0
File Manager Pro – Filester v2.0
2.1.1 trunk 1.6.1 1.7.6 1.8 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 2.0 2.0.1 2.0.2 2.1.0
filester / includes / File_manager / lib / php / libs / GdBmp.php
filester / includes / File_manager / lib / php / libs Last commit date
GdBmp.php 9 months ago
GdBmp.php
484 lines
1 <?php
2 /**
3 * Copyright (c) 2011, oov. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 * - Redistributions of source code must retain the above copyright notice,
7 * this list of conditions and the following disclaimer.
8 * - Redistributions in binary form must reproduce the above copyright notice,
9 * this list of conditions and the following disclaimer in the documentation
10 * and/or other materials provided with the distribution.
11 * - Neither the name of the oov nor the names of its contributors may be used to
12 * endorse or promote products derived from this software without specific prior
13 * written permission.
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23 * POSSIBILITY OF SUCH DAMAGE.
24 * bmp ファイルを GD で使えるように
25 * 使用例:
26 * //ファイルから読み込む場合はGDでPNGなどを読み込むのと同じような方法で可
27 * $image = imagecreatefrombmp("test.bmp");
28 * imagedestroy($image);
29 * //文字列から読み込む場合は以下の方法で可
30 * $image = GdBmp::loadFromString(file_get_contents("test.bmp"));
31 * //自動判定されるので破損ファイルでなければこれでも上手くいく
32 * //$image = imagecreatefrombmp(file_get_contents("test.bmp"));
33 * imagedestroy($image);
34 * //その他任意のストリームからの読み込みも可能
35 * $stream = fopen("http://127.0.0.1/test.bmp");
36 * $image = GdBmp::loadFromStream($stream);
37 * //自動判定されるのでこれでもいい
38 * //$image = imagecreatefrombmp($stream);
39 * fclose($stream);
40 * imagedestroy($image);
41 * 対応フォーマット
42 * 1bit
43 * 4bit
44 * 4bitRLE
45 * 8bit
46 * 8bitRLE
47 * 16bit(任意のビットフィールド)
48 * 24bit
49 * 32bit(任意のビットフィールド)
50 * BITMAPINFOHEADER の biCompression が BI_PNG / BI_JPEG の画像
51 * すべての形式でトップダウン/ボトムアップの両方をサポート
52 * 特殊なビットフィールドでもビットフィールドデータが正常なら読み込み可能
53 * 以下のものは非対応
54 * BITMAPV4HEADER と BITMAPV5HEADER に含まれる色空間に関する様�
55 な機能
56 *
57 * @param $filename_or_stream_or_binary
58 *
59 * @return bool|resource
60 */
61
62 if (!function_exists('imagecreatefrombmp')) {
63 function imagecreatefrombmp($filename_or_stream_or_binary)
64 {
65 return elFinderLibGdBmp::load($filename_or_stream_or_binary);
66 }
67 }
68
69 class elFinderLibGdBmp
70 {
71 public static function load($filename_or_stream_or_binary)
72 {
73 if (is_resource($filename_or_stream_or_binary)) {
74 return self::loadFromStream($filename_or_stream_or_binary);
75 } else if (is_string($filename_or_stream_or_binary) && strlen($filename_or_stream_or_binary) >= 26) {
76 $bfh = unpack("vtype/Vsize", $filename_or_stream_or_binary);
77 if ($bfh["type"] == 0x4d42 && ($bfh["size"] == 0 || $bfh["size"] == strlen($filename_or_stream_or_binary))) {
78 return self::loadFromString($filename_or_stream_or_binary);
79 }
80 }
81 return self::loadFromFile($filename_or_stream_or_binary);
82 }
83
84 public static function loadFromFile($filename)
85 {
86 $fp = fopen($filename, "rb");
87 if ($fp === false) {
88 return false;
89 }
90
91 $bmp = self::loadFromStream($fp);
92
93 fclose($fp);
94 return $bmp;
95 }
96
97 public static function loadFromString($str)
98 {
99 //data scheme より古いバージョンから対応しているようなので php://memory を使う
100 $fp = fopen("php://memory", "r+b");
101 if ($fp === false) {
102 return false;
103 }
104
105 if (fwrite($fp, $str) != strlen($str)) {
106 fclose($fp);
107 return false;
108 }
109
110 if (fseek($fp, 0) === -1) {
111 fclose($fp);
112 return false;
113 }
114
115 $bmp = self::loadFromStream($fp);
116
117 fclose($fp);
118 return $bmp;
119 }
120
121 public static function loadFromStream($stream)
122 {
123 $buf = fread($stream, 14); //2+4+2+2+4
124 if ($buf === false) {
125 return false;
126 }
127
128 //シグネチャチェック
129 if ($buf[0] != 'B' || $buf[1] != 'M') {
130 return false;
131 }
132
133 $bitmap_file_header = unpack(
134 //BITMAPFILEHEADER構造体
135 "vtype/" .
136 "Vsize/" .
137 "vreserved1/" .
138 "vreserved2/" .
139 "Voffbits", $buf
140 );
141
142 return self::loadFromStreamAndFileHeader($stream, $bitmap_file_header);
143 }
144
145 public static function loadFromStreamAndFileHeader($stream, array $bitmap_file_header)
146 {
147 if ($bitmap_file_header["type"] != 0x4d42) {
148 return false;
149 }
150
151 //�
152 報ヘッダサイズを�
153 �に形式を区別して読み込み
154 $buf = fread($stream, 4);
155 if ($buf === false) {
156 return false;
157 }
158 list(, $header_size) = unpack("V", $buf);
159
160
161 if ($header_size == 12) {
162 $buf = fread($stream, $header_size - 4);
163 if ($buf === false) {
164 return false;
165 }
166
167 extract(unpack(
168 //BITMAPCOREHEADER構造体 - OS/2 Bitmap
169 "vwidth/" .
170 "vheight/" .
171 "vplanes/" .
172 "vbit_count", $buf
173 ));
174 //飛んでこない分は 0 で初期化しておく
175 $clr_used = $clr_important = $alpha_mask = $compression = 0;
176
177 //マスク類は初期化されないのでここで割り当てておく
178 $red_mask = 0x00ff0000;
179 $green_mask = 0x0000ff00;
180 $blue_mask = 0x000000ff;
181 } else if (124 < $header_size || $header_size < 40) {
182 //未知の形式
183 return false;
184 } else {
185 //この時点で36バイト読めることまではわかっている
186 $buf = fread($stream, 36); //既に読んだ部分は除外しつつBITMAPINFOHEADERのサイズだけ読む
187 if ($buf === false) {
188 return false;
189 }
190
191 //BITMAPINFOHEADER構造体 - Windows Bitmap
192 extract(unpack(
193 "Vwidth/" .
194 "Vheight/" .
195 "vplanes/" .
196 "vbit_count/" .
197 "Vcompression/" .
198 "Vsize_image/" .
199 "Vx_pels_per_meter/" .
200 "Vy_pels_per_meter/" .
201 "Vclr_used/" .
202 "Vclr_important", $buf
203 ));
204 /**
205 * @var integer $width
206 * @var integer $height
207 * @var integer $planes
208 * @var integer $bit_count
209 * @var integer $compression
210 * @var integer $size_image
211 * @var integer $x_pels_per_meter
212 * @var integer $y_pels_per_meter
213 * @var integer $clr_used
214 * @var integer $clr_important
215 */
216 //負の整数を受け取る可能性があるものは自前で変換する
217 if ($width & 0x80000000) {
218 $width = -(~$width & 0xffffffff) - 1;
219 }
220 if ($height & 0x80000000) {
221 $height = -(~$height & 0xffffffff) - 1;
222 }
223 if ($x_pels_per_meter & 0x80000000) {
224 $x_pels_per_meter = -(~$x_pels_per_meter & 0xffffffff) - 1;
225 }
226 if ($y_pels_per_meter & 0x80000000) {
227 $y_pels_per_meter = -(~$y_pels_per_meter & 0xffffffff) - 1;
228 }
229
230 //ファイルによっては BITMAPINFOHEADER のサイズがおかしい(書き込み間違い?)ケースがある
231 //自分でファイルサイズを�
232 �に逆算することで回避できることもあるので再計算できそうなら正当性を調べる
233 //シークできないストリームの場合�
234 �体のファイルサイズは取得できないので、$bitmap_file_headerにサイズ申告がなければやらない
235 if ($bitmap_file_header["size"] != 0) {
236 $colorsize = $bit_count == 1 || $bit_count == 4 || $bit_count == 8 ? ($clr_used ? $clr_used : pow(2, $bit_count)) << 2 : 0;
237 $bodysize = $size_image ? $size_image : ((($width * $bit_count + 31) >> 3) & ~3) * abs($height);
238 $calcsize = $bitmap_file_header["size"] - $bodysize - $colorsize - 14;
239
240 //本来であれば一致するはずなのに合わない時は、値がおかしくなさそうなら(BITMAPV5HEADERの範囲�
241 なら)計算して求めた値を採用する
242 if ($header_size < $calcsize && 40 <= $header_size && $header_size <= 124) {
243 $header_size = $calcsize;
244 }
245 }
246
247 //BITMAPV4HEADER や BITMAPV5HEADER の場合まだ読むべきデータが残っている可能性がある
248 if ($header_size - 40 > 0) {
249 $buf = fread($stream, $header_size - 40);
250 if ($buf === false) {
251 return false;
252 }
253
254 extract(unpack(
255 //BITMAPV4HEADER構造体(Windows95以降)
256 //BITMAPV5HEADER構造体(Windows98/2000以降)
257 "Vred_mask/" .
258 "Vgreen_mask/" .
259 "Vblue_mask/" .
260 "Valpha_mask", $buf . str_repeat("\x00", 120)
261 ));
262 } else {
263 $alpha_mask = $red_mask = $green_mask = $blue_mask = 0;
264 }
265
266 //パレットがないがカラーマスクもない時
267 if (
268 ($bit_count == 16 || $bit_count == 24 || $bit_count == 32) &&
269 $compression == 0 &&
270 $red_mask == 0 && $green_mask == 0 && $blue_mask == 0
271 ) {
272 //もしカラーマスクを所持していない場合は
273 //規定のカラーマスクを適用する
274 switch ($bit_count) {
275 case 16:
276 $red_mask = 0x7c00;
277 $green_mask = 0x03e0;
278 $blue_mask = 0x001f;
279 break;
280 case 24:
281 case 32:
282 $red_mask = 0x00ff0000;
283 $green_mask = 0x0000ff00;
284 $blue_mask = 0x000000ff;
285 break;
286 }
287 }
288 }
289
290 if (
291 ($width == 0) ||
292 ($height == 0) ||
293 ($planes != 1) ||
294 (($alpha_mask & $red_mask) != 0) ||
295 (($alpha_mask & $green_mask) != 0) ||
296 (($alpha_mask & $blue_mask) != 0) ||
297 (($red_mask & $green_mask) != 0) ||
298 (($red_mask & $blue_mask) != 0) ||
299 (($green_mask & $blue_mask) != 0)
300 ) {
301 //不正な画像
302 return false;
303 }
304
305 //BI_JPEG と BI_PNG の場合は jpeg/png がそのまま�
306 �ってるだけなのでそのまま取り出してデコードする
307 if ($compression == 4 || $compression == 5) {
308 $buf = stream_get_contents($stream, $size_image);
309 if ($buf === false) {
310 return false;
311 }
312 return imagecreatefromstring($buf);
313 }
314
315 //画像本体の読み出し
316 //1行のバイト数
317 $line_bytes = (($width * $bit_count + 31) >> 3) & ~3;
318 //�
319 �体の行数
320 $lines = abs($height);
321 //y軸進行量(ボトムアップかトップダウンか)
322 $y = $height > 0 ? $lines - 1 : 0;
323 $line_step = $height > 0 ? -1 : 1;
324
325 //256色以下の画像か?
326 if ($bit_count == 1 || $bit_count == 4 || $bit_count == 8) {
327 $img = imagecreate($width, $lines);
328
329 //画像データの前にパレットデータがあるのでパレットを作成する
330 $palette_size = $header_size == 12 ? 3 : 4; //OS/2形式の場合は x に相当する箇所のデータは最初から確保されていない
331 $colors = $clr_used ? $clr_used : pow(2, $bit_count); //色数
332 $palette = array();
333 for ($i = 0; $i < $colors; ++$i) {
334 $buf = fread($stream, $palette_size);
335 if ($buf === false) {
336 imagedestroy($img);
337 return false;
338 }
339 extract(unpack("Cb/Cg/Cr/Cx", $buf . "\x00"));
340 /**
341 * @var integer $b
342 * @var integer $g
343 * @var integer $r
344 * @var integer $x
345 */
346 $palette[] = imagecolorallocate($img, $r, $g, $b);
347 }
348
349 $shift_base = 8 - $bit_count;
350 $mask = ((1 << $bit_count) - 1) << $shift_base;
351
352 //圧縮されている場合とされていない場合でデコード処理が大きく変わる
353 if ($compression == 1 || $compression == 2) {
354 $x = 0;
355 $qrt_mod2 = $bit_count >> 2 & 1;
356 for (; ;) {
357 //もし描写�
358 �が範囲外になっている場合デコード処理がおかしくなっているので抜ける
359 //変なデータが渡されたとしても最悪なケースで255回程度の無駄なので目を瞑る
360 if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
361 imagedestroy($img);
362 return false;
363 }
364 $buf = fread($stream, 1);
365 if ($buf === false) {
366 imagedestroy($img);
367 return false;
368 }
369 switch ($buf) {
370 case "\x00":
371 $buf = fread($stream, 1);
372 if ($buf === false) {
373 imagedestroy($img);
374 return false;
375 }
376 switch ($buf) {
377 case "\x00": //EOL
378 $y += $line_step;
379 $x = 0;
380 break;
381 case "\x01": //EOB
382 $y = 0;
383 $x = 0;
384 break 3;
385 case "\x02": //MOV
386 $buf = fread($stream, 2);
387 if ($buf === false) {
388 imagedestroy($img);
389 return false;
390 }
391 list(, $xx, $yy) = unpack("C2", $buf);
392 $x += $xx;
393 $y += $yy * $line_step;
394 break;
395 default: //ABS
396 list(, $pixels) = unpack("C", $buf);
397 $bytes = ($pixels >> $qrt_mod2) + ($pixels & $qrt_mod2);
398 $buf = fread($stream, ($bytes + 1) & ~1);
399 if ($buf === false) {
400 imagedestroy($img);
401 return false;
402 }
403 for ($i = 0, $pos = 0; $i < $pixels; ++$i, ++$x, $pos += $bit_count) {
404 list(, $c) = unpack("C", $buf[$pos >> 3]);
405 $b = $pos & 0x07;
406 imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
407 }
408 break;
409 }
410 break;
411 default:
412 $buf2 = fread($stream, 1);
413 if ($buf2 === false) {
414 imagedestroy($img);
415 return false;
416 }
417 list(, $size, $c) = unpack("C2", $buf . $buf2);
418 for ($i = 0, $pos = 0; $i < $size; ++$i, ++$x, $pos += $bit_count) {
419 $b = $pos & 0x07;
420 imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
421 }
422 break;
423 }
424 }
425 } else {
426 for ($line = 0; $line < $lines; ++$line, $y += $line_step) {
427 $buf = fread($stream, $line_bytes);
428 if ($buf === false) {
429 imagedestroy($img);
430 return false;
431 }
432
433 $pos = 0;
434 for ($x = 0; $x < $width; ++$x, $pos += $bit_count) {
435 list(, $c) = unpack("C", $buf[$pos >> 3]);
436 $b = $pos & 0x7;
437 imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
438 }
439 }
440 }
441 } else {
442 $img = imagecreatetruecolor($width, $lines);
443 imagealphablending($img, false);
444 if ($alpha_mask) {
445 //αデータがあるので透過�
446 報も保存できるように
447 imagesavealpha($img, true);
448 }
449
450 //x軸進行量
451 $pixel_step = $bit_count >> 3;
452 $alpha_max = $alpha_mask ? 0x7f : 0x00;
453 $alpha_mask_r = $alpha_mask ? 1 / $alpha_mask : 1;
454 $red_mask_r = $red_mask ? 1 / $red_mask : 1;
455 $green_mask_r = $green_mask ? 1 / $green_mask : 1;
456 $blue_mask_r = $blue_mask ? 1 / $blue_mask : 1;
457
458 for ($line = 0; $line < $lines; ++$line, $y += $line_step) {
459 $buf = fread($stream, $line_bytes);
460 if ($buf === false) {
461 imagedestroy($img);
462 return false;
463 }
464
465 $pos = 0;
466 for ($x = 0; $x < $width; ++$x, $pos += $pixel_step) {
467 list(, $c) = unpack("V", substr($buf, $pos, $pixel_step) . "\x00\x00");
468 $a_masked = $c & $alpha_mask;
469 $r_masked = $c & $red_mask;
470 $g_masked = $c & $green_mask;
471 $b_masked = $c & $blue_mask;
472 $a = $alpha_max - ((($a_masked << 7) - $a_masked) * $alpha_mask_r);
473 $r = (($r_masked << 8) - $r_masked) * $red_mask_r;
474 $g = (($g_masked << 8) - $g_masked) * $green_mask_r;
475 $b = (($b_masked << 8) - $b_masked) * $blue_mask_r;
476 imagesetpixel($img, $x, $y, ($a << 24) | ($r << 16) | ($g << 8) | $b);
477 }
478 }
479 imagealphablending($img, true); //デフォルト値に戻しておく
480 }
481 return $img;
482 }
483 }
484