elFinderLibGdBmp::loadFromStream()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 22
rs 9.568
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 71 and the first side effect is on line 2.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
defined('PH7') or exit('Restricted access');
3
if (!\PH7\Admin::auth()) exit('Restricted access'); // Accessible only for admins
4
5
/**
6
 * Copyright (c) 2011, oov. All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without modification,
9
 * are permitted provided that the following conditions are met:
10
 *
11
 *  - Redistributions of source code must retain the above copyright notice,
12
 *    this list of conditions and the following disclaimer.
13
 *  - Redistributions in binary form must reproduce the above copyright notice,
14
 *    this list of conditions and the following disclaimer in the documentation
15
 *    and/or other materials provided with the distribution.
16
 *  - Neither the name of the oov nor the names of its contributors may be used to
17
 *    endorse or promote products derived from this software without specific prior
18
 *    written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
26
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 * POSSIBILITY OF SUCH DAMAGE.
30
 *
31
 * bmp ファイルを GD で使えるように
32
 *
33
 * 使用例:
34
 *   //ファイルから読み込む場合はGDでPNGなどを読み込むのと同じような方法で可
35
 *   $image = imagecreatefrombmp("test.bmp");
36
 *   imagedestroy($image);
37
 *
38
 *   //文字列から読み込む場合は以下の方法で可
39
 *   $image = GdBmp::loadFromString(file_get_contents("test.bmp"));
40
 *   //自動判定されるので破損ファイルでなければこれでも上手くいく
41
 *   //$image = imagecreatefrombmp(file_get_contents("test.bmp"));
42
 *   imagedestroy($image);
43
 *
44
 *   //その他任意のストリームからの読み込みも可能
45
 *   $stream = fopen("http://127.0.0.1/test.bmp");
46
 *   $image = GdBmp::loadFromStream($stream);
47
 *   //自動判定されるのでこれでもいい
48
 *   //$image = imagecreatefrombmp($stream);
49
 *   fclose($stream);
50
 *   imagedestroy($image);
51
 *
52
 * 対応フォーマット
53
 *   1bit
54
 *   4bit
55
 *   4bitRLE
56
 *   8bit
57
 *   8bitRLE
58
 *   16bit(任意のビットフィールド)
59
 *   24bit
60
 *   32bit(任意のビットフィールド)
61
 *   BITMAPINFOHEADER の biCompression が BI_PNG / BI_JPEG の画像
62
 *   すべての形式でトップダウン/ボトムアップの両方をサポート
63
 *   特殊なビットフィールドでもビットフィールドデータが正常なら読み込み可能
64
 *
65
 * 以下のものは非対応
66
 *   BITMAPV4HEADER と BITMAPV5HEADER に含まれる色空間に関する様々な機能
67
 * @param $filename_or_stream_or_binary
68
 * @return bool|resource
69
 */
70
71
function imagecreatefrombmp($filename_or_stream_or_binary){
72
    return elFinderLibGdBmp::load($filename_or_stream_or_binary);
73
}
74
75
class elFinderLibGdBmp{
76
    public static function load($filename_or_stream_or_binary){
77
        if (is_resource($filename_or_stream_or_binary)){
78
            return self::loadFromStream($filename_or_stream_or_binary);
79
        } else if (is_string($filename_or_stream_or_binary) && strlen($filename_or_stream_or_binary) >= 26){
80
            $bfh = unpack("vtype/Vsize", $filename_or_stream_or_binary);
81
            if ($bfh["type"] == 0x4d42 && ($bfh["size"] == 0 || $bfh["size"] == strlen($filename_or_stream_or_binary))){
82
                return self::loadFromString($filename_or_stream_or_binary);
83
            }
84
        }
85
        return self::loadFromFile($filename_or_stream_or_binary);
86
    }
87
    public static function loadFromFile($filename){
88
        $fp = fopen($filename, "rb");
89
        if ($fp === false){
90
            return false;
91
        }
92
93
        $bmp = self::loadFromStream($fp);
94
95
        fclose($fp);
96
        return $bmp;
97
    }
98
99
    public static function loadFromString($str){
100
        //data scheme より古いバージョンから対応しているようなので php://memory を使う
101
        $fp = fopen("php://memory", "r+b");
102
        if ($fp === false){
103
            return false;
104
        }
105
106
        if (fwrite($fp, $str) != strlen($str)){
107
            fclose($fp);
108
            return false;
109
        }
110
111
        if (fseek($fp, 0) === -1){
112
            fclose($fp);
113
            return false;
114
        }
115
116
        $bmp = self::loadFromStream($fp);
117
118
        fclose($fp);
119
        return $bmp;
120
    }
121
122
    public static function loadFromStream($stream){
123
        $buf = fread($stream, 14); //2+4+2+2+4
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
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
        if ($bitmap_file_header["type"] != 0x4d42){
147
            return false;
148
        }
149
150
        //情報ヘッダサイズを元に形式を区別して読み込み
151
        $buf = fread($stream, 4);
152
        if ($buf === false){
153
            return false;
154
        }
155
        list(,$header_size) = unpack("V", $buf);
156
157
158
        if ($header_size == 12){
159
            $buf = fread($stream, $header_size - 4);
160
            if ($buf === false){
161
                return false;
162
            }
163
164
            extract(unpack(
0 ignored issues
show
Bug introduced by
unpack('vwidth/' . 'vhei...' . 'vbit_count', $buf) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
165
                //BITMAPCOREHEADER構造体 - OS/2 Bitmap
166
                "vwidth/".
167
                "vheight/".
168
                "vplanes/".
169
                "vbit_count", $buf
170
            ));
171
            //飛んでこない分は 0 で初期化しておく
172
            $clr_used = $clr_important = $alpha_mask = $compression = 0;
0 ignored issues
show
Unused Code introduced by
$clr_important is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
173
174
            //マスク類は初期化されないのでここで割り当てておく
175
            $red_mask   = 0x00ff0000;
176
            $green_mask = 0x0000ff00;
177
            $blue_mask  = 0x000000ff;
178
        } else if (124 < $header_size || $header_size < 40) {
179
            //未知の形式
180
            return false;
181
        } else {
182
            //この時点で36バイト読めることまではわかっている
183
            $buf = fread($stream, 36); //既に読んだ部分は除外しつつBITMAPINFOHEADERのサイズだけ読む
184
            if ($buf === false){
185
                return false;
186
            }
187
188
            //BITMAPINFOHEADER構造体 - Windows Bitmap
189
            extract(unpack(
0 ignored issues
show
Bug introduced by
unpack('Vwidth/' . 'Vhei...'Vclr_important', $buf) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
190
                "Vwidth/".
191
                "Vheight/".
192
                "vplanes/".
193
                "vbit_count/".
194
                "Vcompression/".
195
                "Vsize_image/".
196
                "Vx_pels_per_meter/".
197
                "Vy_pels_per_meter/".
198
                "Vclr_used/".
199
                "Vclr_important", $buf
200
            ));
201
            //負の整数を受け取る可能性があるものは自前で変換する
202
            if ($width  & 0x80000000){ $width  = -(~$width  & 0xffffffff) - 1; }
203
            if ($height & 0x80000000){ $height = -(~$height & 0xffffffff) - 1; }
204
            if ($x_pels_per_meter & 0x80000000){ $x_pels_per_meter = -(~$x_pels_per_meter & 0xffffffff) - 1; }
0 ignored issues
show
Unused Code introduced by
$x_pels_per_meter is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
205
            if ($y_pels_per_meter & 0x80000000){ $y_pels_per_meter = -(~$y_pels_per_meter & 0xffffffff) - 1; }
0 ignored issues
show
Unused Code introduced by
$y_pels_per_meter is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
206
207
            //ファイルによっては BITMAPINFOHEADER のサイズがおかしい(書き込み間違い?)ケースがある
208
            //自分でファイルサイズを元に逆算することで回避できることもあるので再計算できそうなら正当性を調べる
209
            //シークできないストリームの場合全体のファイルサイズは取得できないので、$bitmap_file_headerにサイズ申告がなければやらない
210
            if ($bitmap_file_header["size"] != 0){
211
                $colorsize = $bit_count == 1 || $bit_count == 4 || $bit_count == 8 ? ($clr_used ? $clr_used : pow(2, $bit_count))<<2 : 0;
212
                $bodysize = $size_image ? $size_image : ((($width * $bit_count + 31) >> 3) & ~3) * abs($height);
213
                $calcsize = $bitmap_file_header["size"] - $bodysize - $colorsize - 14;
214
215
                //本来であれば一致するはずなのに合わない時は、値がおかしくなさそうなら(BITMAPV5HEADERの範囲内なら)計算して求めた値を採用する
216
                if ($header_size < $calcsize && 40 <= $header_size && $header_size <= 124){
217
                    $header_size = $calcsize;
218
                }
219
            }
220
221
            //BITMAPV4HEADER や BITMAPV5HEADER の場合まだ読むべきデータが残っている可能性がある
222
            if ($header_size - 40 > 0){
223
                $buf = fread($stream, $header_size - 40);
224
                if ($buf === false){
225
                    return false;
226
                }
227
228
                extract(unpack(
0 ignored issues
show
Bug introduced by
unpack('Vred_mask/' . 'V.... str_repeat('', 120)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
229
                    //BITMAPV4HEADER構造体(Windows95以降)
230
                    //BITMAPV5HEADER構造体(Windows98/2000以降)
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
231
                    "Vred_mask/".
232
                    "Vgreen_mask/".
233
                    "Vblue_mask/".
234
                    "Valpha_mask", $buf . str_repeat("\x00", 120)
235
                ));
236
            } else {
237
                $alpha_mask = $red_mask = $green_mask = $blue_mask = 0;
238
            }
239
240
            //パレットがないがカラーマスクもない時
241
            if (
242
                ($bit_count == 16 || $bit_count == 24 || $bit_count == 32)&&
243
                $compression == 0 &&
244
                $red_mask == 0 && $green_mask == 0 && $blue_mask == 0
245
            ){
246
                //もしカラーマスクを所持していない場合は
247
                //規定のカラーマスクを適用する
248
                switch($bit_count){
249
                case 16:
250
                    $red_mask   = 0x7c00;
251
                    $green_mask = 0x03e0;
252
                    $blue_mask  = 0x001f;
253
                    break;
254
                case 24:
255
                case 32:
256
                    $red_mask   = 0x00ff0000;
257
                    $green_mask = 0x0000ff00;
258
                    $blue_mask  = 0x000000ff;
259
                    break;
260
                }
261
            }
262
        }
263
264
        if (
265
            ($width  == 0)||
266
            ($height == 0)||
267
            ($planes != 1)||
268
            (($alpha_mask & $red_mask  ) != 0)||
269
            (($alpha_mask & $green_mask) != 0)||
270
            (($alpha_mask & $blue_mask ) != 0)||
271
            (($red_mask   & $green_mask) != 0)||
272
            (($red_mask   & $blue_mask ) != 0)||
273
            (($green_mask & $blue_mask ) != 0)
274
        ){
275
            //不正な画像
276
            return false;
277
        }
278
279
        //BI_JPEG と BI_PNG の場合は jpeg/png がそのまま入ってるだけなのでそのまま取り出してデコードする
280
        if ($compression == 4 || $compression == 5){
281
            $buf = stream_get_contents($stream, $size_image);
282
            if ($buf === false){
283
                return false;
284
            }
285
            return imagecreatefromstring($buf);
286
        }
287
288
        //画像本体の読み出し
289
        //1行のバイト数
290
        $line_bytes = (($width * $bit_count + 31) >> 3) & ~3;
291
        //全体の行数
292
        $lines = abs($height);
293
        //y軸進行量(ボトムアップかトップダウンか)
294
        $y = $height > 0 ? $lines-1 : 0;
295
        $line_step = $height > 0 ? -1 : 1;
296
297
        //256色以下の画像か?
298
        if ($bit_count == 1 || $bit_count == 4 || $bit_count == 8){
299
            $img = imagecreate($width, $lines);
300
301
            //画像データの前にパレットデータがあるのでパレットを作成する
302
            $palette_size = $header_size == 12 ? 3 : 4; //OS/2形式の場合は x に相当する箇所のデータは最初から確保されていない
303
            $colors = $clr_used ? $clr_used : pow(2, $bit_count); //色数
304
            $palette = array();
305
            for($i = 0; $i < $colors; ++$i){
306
                $buf = fread($stream, $palette_size);
307
                if ($buf === false){
308
                    imagedestroy($img);
309
                    return false;
310
                }
311
                extract(unpack("Cb/Cg/Cr/Cx", $buf . "\x00"));
0 ignored issues
show
Bug introduced by
unpack('Cb/Cg/Cr/Cx', $buf . '') cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
312
                $palette[] = imagecolorallocate($img, $r, $g, $b);
313
            }
314
315
            $shift_base = 8 - $bit_count;
316
            $mask = ((1 << $bit_count) - 1) << $shift_base;
317
318
            //圧縮されている場合とされていない場合でデコード処理が大きく変わる
319
            if ($compression == 1 || $compression == 2){
320
                $x = 0;
321
                $qrt_mod2 = $bit_count >> 2 & 1;
322
                for(;;){
323
                    //もし描写先が範囲外になっている場合デコード処理がおかしくなっているので抜ける
324
                    //変なデータが渡されたとしても最悪なケースで255回程度の無駄なので目を瞑る
325
                    if ($x < -1 || $x > $width || $y < -1 || $y > $height){
326
                        imagedestroy($img);
327
                        return false;
328
                    }
329
                    $buf = fread($stream, 1);
330
                    if ($buf === false){
331
                        imagedestroy($img);
332
                        return false;
333
                    }
334
                    switch($buf){
335
                    case "\x00":
336
                        $buf = fread($stream, 1);
337
                        if ($buf === false){
338
                            imagedestroy($img);
339
                            return false;
340
                        }
341
                        switch($buf){
342
                        case "\x00": //EOL
343
                            $y += $line_step;
344
                            $x = 0;
345
                            break;
346
                        case "\x01": //EOB
347
                            $y = 0;
0 ignored issues
show
Unused Code introduced by
$y is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
348
                            $x = 0;
0 ignored issues
show
Unused Code introduced by
$x is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
349
                            break 3;
350
                        case "\x02": //MOV
351
                            $buf = fread($stream, 2);
352
                            if ($buf === false){
353
                                imagedestroy($img);
354
                                return false;
355
                            }
356
                            list(,$xx, $yy) = unpack("C2", $buf);
357
                            $x += $xx;
358
                            $y += $yy * $line_step;
359
                            break;
360
                        default:     //ABS
361
                            list(,$pixels) = unpack("C", $buf);
362
                            $bytes = ($pixels >> $qrt_mod2) + ($pixels & $qrt_mod2);
363
                            $buf = fread($stream, ($bytes + 1) & ~1);
364
                            if ($buf === false){
365
                                imagedestroy($img);
366
                                return false;
367
                            }
368
                            for ($i = 0, $pos = 0; $i < $pixels; ++$i, ++$x, $pos += $bit_count){
369
                                list(,$c) = unpack("C", $buf[$pos >> 3]);
370
                                $b = $pos & 0x07;
371
                                imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
372
                            }
373
                            break;
374
                        }
375
                        break;
376
                    default:
377
                        $buf2 = fread($stream, 1);
378
                        if ($buf2 === false){
379
                            imagedestroy($img);
380
                            return false;
381
                        }
382
                        list(,$size, $c) = unpack("C2", $buf . $buf2);
383
                        for($i = 0, $pos = 0; $i < $size; ++$i, ++$x, $pos += $bit_count){
384
                            $b = $pos & 0x07;
385
                            imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
386
                        }
387
                        break;
388
                    }
389
                }
390
            } else {
391
                for ($line = 0; $line < $lines; ++$line, $y += $line_step){
392
                    $buf = fread($stream, $line_bytes);
393
                    if ($buf === false){
394
                        imagedestroy($img);
395
                        return false;
396
                    }
397
398
                    $pos = 0;
399
                    for ($x = 0; $x < $width; ++$x, $pos += $bit_count){
400
                        list(,$c) = unpack("C", $buf[$pos >> 3]);
401
                        $b = $pos & 0x7;
402
                        imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
403
                    }
404
                }
405
            }
406
        } else {
407
            $img = imagecreatetruecolor($width, $lines);
408
            imagealphablending($img, false);
409
            if ($alpha_mask)
410
            {
411
                //αデータがあるので透過情報も保存できるように
412
                imagesavealpha($img, true);
413
            }
414
415
            //x軸進行量
416
            $pixel_step = $bit_count >> 3;
417
            $alpha_max    = $alpha_mask ? 0x7f : 0x00;
418
            $alpha_mask_r = $alpha_mask ? 1/$alpha_mask : 1;
419
            $red_mask_r   = $red_mask   ? 1/$red_mask   : 1;
420
            $green_mask_r = $green_mask ? 1/$green_mask : 1;
421
            $blue_mask_r  = $blue_mask  ? 1/$blue_mask  : 1;
422
423
            for ($line = 0; $line < $lines; ++$line, $y += $line_step){
424
                $buf = fread($stream, $line_bytes);
425
                if ($buf === false){
426
                    imagedestroy($img);
427
                    return false;
428
                }
429
430
                $pos = 0;
431
                for ($x = 0; $x < $width; ++$x, $pos += $pixel_step){
432
                    list(,$c) = unpack("V", substr($buf, $pos, $pixel_step). "\x00\x00");
433
                    $a_masked = $c & $alpha_mask;
434
                    $r_masked = $c & $red_mask;
435
                    $g_masked = $c & $green_mask;
436
                    $b_masked = $c & $blue_mask;
437
                    $a = $alpha_max - ((($a_masked<<7) - $a_masked) * $alpha_mask_r);
438
                    $r = (($r_masked<<8) - $r_masked) * $red_mask_r;
439
                    $g = (($g_masked<<8) - $g_masked) * $green_mask_r;
440
                    $b = (($b_masked<<8) - $b_masked) * $blue_mask_r;
441
                    imagesetpixel($img, $x, $y, ($a<<24)|($r<<16)|($g<<8)|$b);
442
                }
443
            }
444
            imagealphablending($img, true); //デフォルト値に戻しておく
445
        }
446
        return $img;
447
    }
448
}
449