elFinderLibGdBmp   D
last analyzed

Complexity

Total Complexity 101

Size/Duplication

Total Lines 403
Duplicated Lines 2.23 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 9
loc 403
rs 4.8717
c 0
b 0
f 0
wmc 101
lcom 1
cbo 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
B load() 0 13 7
A loadFromFile() 0 13 2
B loadFromString() 0 26 4
B loadFromStream() 0 23 4
F loadFromStreamAndFileHeader() 9 321 84

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like elFinderLibGdBmp often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use elFinderLibGdBmp, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Copyright (c) 2011, oov. All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without modification,
6
 * are permitted provided that the following conditions are met:
7
 *
8
 *  - Redistributions of source code must retain the above copyright notice,
9
 *    this list of conditions and the following disclaimer.
10
 *  - Redistributions in binary form must reproduce the above copyright notice,
11
 *    this list of conditions and the following disclaimer in the documentation
12
 *    and/or other materials provided with the distribution.
13
 *  - Neither the name of the oov nor the names of its contributors may be used to
14
 *    endorse or promote products derived from this software without specific prior
15
 *    written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
 * POSSIBILITY OF SUCH DAMAGE.
27
 *
28
 * bmp ファイルを GD で使えるように
29
 *
30
 * 使用例:
31
 *   //ファイルから読み込む場合はGDでPNGなどを読み込むのと同じような方法で可
32
 *   $image = imagecreatefrombmp("test.bmp");
33
 *   imagedestroy($image);
34
 *
35
 *   //文字列から読み込む場合は以下の方法で可
36
 *   $image = GdBmp::loadFromString(file_get_contents("test.bmp"));
37
 *   //自動判定されるので破損ファイルでなければこれでも上手くいく
38
 *   //$image = imagecreatefrombmp(file_get_contents("test.bmp"));
39
 *   imagedestroy($image);
40
 *
41
 *   //その他任意のストリームからの読み込みも可能
42
 *   $stream = fopen("http://127.0.0.1/test.bmp");
43
 *   $image = GdBmp::loadFromStream($stream);
44
 *   //自動判定されるのでこれでもいい
45
 *   //$image = imagecreatefrombmp($stream);
46
 *   fclose($stream);
47
 *   imagedestroy($image);
48
 *
49
 * 対応フォーマット
50
 *   1bit
51
 *   4bit
52
 *   4bitRLE
53
 *   8bit
54
 *   8bitRLE
55
 *   16bit(任意のビットフィールド)
56
 *   24bit
57
 *   32bit(任意のビットフィールド)
58
 *   BITMAPINFOHEADER の biCompression が BI_PNG / BI_JPEG の画像
59
 *   すべての形式でトップダウン/ボトムアップの両方をサポート
60
 *   特殊なビットフィールドでもビットフィールドデータが正常なら読み込み可能
61
 *
62
 * 以下のものは非対応
63
 *   BITMAPV4HEADER と BITMAPV5HEADER に含まれる色空間に関する様�
64
 * な機能
65
 * @param $filename_or_stream_or_binary
66
 * @return bool|resource
67
 */
68
function imagecreatefrombmp($filename_or_stream_or_binary)
69
{
70
    return elFinderLibGdBmp::load($filename_or_stream_or_binary);
71
}
72
73
class elFinderLibGdBmp
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
74
{
75
    public static function load($filename_or_stream_or_binary)
76
    {
77
        if (is_resource($filename_or_stream_or_binary)) {
78
            return self::loadFromStream($filename_or_stream_or_binary);
79
        } elseif (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
86
        return self::loadFromFile($filename_or_stream_or_binary);
87
    }
88
89
    public static function loadFromFile($filename)
90
    {
91
        $fp = fopen($filename, 'rb');
92
        if ($fp === false) {
93
            return false;
94
        }
95
96
        $bmp = self::loadFromStream($fp);
97
98
        fclose($fp);
99
100
        return $bmp;
101
    }
102
103
    public static function loadFromString($str)
104
    {
105
        //data scheme より古いバージョンから対応しているようなので php://memory を使う
106
        $fp = fopen('php://memory', 'r+b');
107
        if ($fp === false) {
108
            return false;
109
        }
110
111
        if (fwrite($fp, $str) != strlen($str)) {
112
            fclose($fp);
113
114
            return false;
115
        }
116
117
        if (fseek($fp, 0) === -1) {
118
            fclose($fp);
119
120
            return false;
121
        }
122
123
        $bmp = self::loadFromStream($fp);
124
125
        fclose($fp);
126
127
        return $bmp;
128
    }
129
130
    public static function loadFromStream($stream)
131
    {
132
        $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...
133
        if ($buf === false) {
134
            return false;
135
        }
136
137
        //シグネチャチェック
138
        if ($buf[0] != 'B' || $buf[1] != 'M') {
139
            return false;
140
        }
141
142
        $bitmap_file_header = unpack(
143
            //BITMAPFILEHEADER構造体
144
            'vtype/'.
145
            'Vsize/'.
146
            'vreserved1/'.
147
            'vreserved2/'.
148
            'Voffbits', $buf
149
        );
150
151
        return self::loadFromStreamAndFileHeader($stream, $bitmap_file_header);
152
    }
153
154
    public static function loadFromStreamAndFileHeader($stream, array $bitmap_file_header)
155
    {
156
        if ($bitmap_file_header['type'] != 0x4d42) {
157
            return false;
158
        }
159
160
        //情報ヘッダサイズを元に形式を区別して読み込み
161
        $buf = fread($stream, 4);
162
        if ($buf === false) {
163
            return false;
164
        }
165
        list(, $header_size) = unpack('V', $buf);
166
167
        if ($header_size == 12) {
168
            $buf = fread($stream, $header_size - 4);
169
            if ($buf === false) {
170
                return false;
171
            }
172
173
            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...
174
                //BITMAPCOREHEADER構造体 - OS/2 Bitmap
175
                'vwidth/'.
176
                'vheight/'.
177
                'vplanes/'.
178
                'vbit_count', $buf
179
            ));
180
            //飛んでこない分は 0 で初期化しておく
181
            $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...
182
183
            //マスク類は初期化されないのでここで割り当てておく
184
            $red_mask = 0x00ff0000;
185
            $green_mask = 0x0000ff00;
186
            $blue_mask = 0x000000ff;
187
        } elseif (124 < $header_size || $header_size < 40) {
188
            //未知の形式
189
            return false;
190
        } else {
191
            //この時点で36バイト読めることまではわかっている
192
            $buf = fread($stream, 36); //既に読んだ部分は除外しつつBITMAPINFOHEADERのサイズだけ読む
193
            if ($buf === false) {
194
                return false;
195
            }
196
197
            //BITMAPINFOHEADER構造体 - Windows Bitmap
198
            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...
199
                'Vwidth/'.
200
                'Vheight/'.
201
                'vplanes/'.
202
                'vbit_count/'.
203
                'Vcompression/'.
204
                'Vsize_image/'.
205
                'Vx_pels_per_meter/'.
206
                'Vy_pels_per_meter/'.
207
                'Vclr_used/'.
208
                'Vclr_important', $buf
209
            ));
210
            //負の整数を受け取る可能性があるものは自前で変換する
211
            if ($width & 0x80000000) {
212
                $width = -(~$width & 0xffffffff) - 1;
213
            }
214
            if ($height & 0x80000000) {
215
                $height = -(~$height & 0xffffffff) - 1;
216
            }
217
            if ($x_pels_per_meter & 0x80000000) {
218
                $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...
219
            }
220
            if ($y_pels_per_meter & 0x80000000) {
221
                $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...
222
            }
223
224
            //ファイルによっては BITMAPINFOHEADER のサイズがおかしい(書き込み間違い?)ケースがある
225
            //自分でファイルサイズを元に逆算することで回避できることもあるので再計算できそうなら正当性を調べる
226
            //シークできないストリームの場合全体のファイルサイズは取得できないので、$bitmap_file_headerにサイズ申告がなければやらない
227
            if ($bitmap_file_header['size'] != 0) {
228
                $colorsize = $bit_count == 1 || $bit_count == 4 || $bit_count == 8 ? ($clr_used ? $clr_used : pow(2, $bit_count)) << 2 : 0;
229
                $bodysize = $size_image ? $size_image : ((($width * $bit_count + 31) >> 3) & ~3) * abs($height);
230
                $calcsize = $bitmap_file_header['size'] - $bodysize - $colorsize - 14;
231
232
                //本来であれば一致するはずなのに合わない時は、値がおかしくなさそうなら(BITMAPV5HEADERの範囲内なら)計算して求めた値を採用する
233
                if ($header_size < $calcsize && 40 <= $header_size && $header_size <= 124) {
234
                    $header_size = $calcsize;
235
                }
236
            }
237
238
            //BITMAPV4HEADER や BITMAPV5HEADER の場合まだ読むべきデータが残っている可能性がある
239
            if ($header_size - 40 > 0) {
240
                $buf = fread($stream, $header_size - 40);
241
                if ($buf === false) {
242
                    return false;
243
                }
244
245
                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...
246
                    //BITMAPV4HEADER構造体(Windows95以降)
247
                    //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...
248
                    'Vred_mask/'.
249
                    'Vgreen_mask/'.
250
                    'Vblue_mask/'.
251
                    'Valpha_mask', $buf.str_repeat("\x00", 120)
252
                ));
253
            } else {
254
                $alpha_mask = $red_mask = $green_mask = $blue_mask = 0;
255
            }
256
257
            //パレットがないがカラーマスクもない時
258
            if (
259
                ($bit_count == 16 || $bit_count == 24 || $bit_count == 32) &&
260
                $compression == 0 &&
261
                $red_mask == 0 && $green_mask == 0 && $blue_mask == 0
262
            ) {
263
                //もしカラーマスクを所持していない場合は
264
                //規定のカラーマスクを適用する
265
                switch ($bit_count) {
266
                case 16:
267
                    $red_mask = 0x7c00;
268
                    $green_mask = 0x03e0;
269
                    $blue_mask = 0x001f;
270
                    break;
271
                case 24:
272
                case 32:
273
                    $red_mask = 0x00ff0000;
274
                    $green_mask = 0x0000ff00;
275
                    $blue_mask = 0x000000ff;
276
                    break;
277
                }
278
            }
279
        }
280
281
        if (
282
            ($width == 0) ||
283
            ($height == 0) ||
284
            ($planes != 1) ||
285
            (($alpha_mask & $red_mask) != 0) ||
286
            (($alpha_mask & $green_mask) != 0) ||
287
            (($alpha_mask & $blue_mask) != 0) ||
288
            (($red_mask & $green_mask) != 0) ||
289
            (($red_mask & $blue_mask) != 0) ||
290
            (($green_mask & $blue_mask) != 0)
291
        ) {
292
            //不正な画像
293
            return false;
294
        }
295
296
        //BI_JPEG と BI_PNG の場合は jpeg/png がそのまま入ってるだけなのでそのまま取り出してデコードする
297
        if ($compression == 4 || $compression == 5) {
298
            $buf = stream_get_contents($stream, $size_image);
299
            if ($buf === false) {
300
                return false;
301
            }
302
303
            return imagecreatefromstring($buf);
304
        }
305
306
        //画像本体の読み出し
307
        //1行のバイト数
308
        $line_bytes = (($width * $bit_count + 31) >> 3) & ~3;
309
        //全体の行数
310
        $lines = abs($height);
311
        //y軸進行量(ボトムアップかトップダウンか)
312
        $y = $height > 0 ? $lines - 1 : 0;
313
        $line_step = $height > 0 ? -1 : 1;
314
315
        //256色以下の画像か?
316
        if ($bit_count == 1 || $bit_count == 4 || $bit_count == 8) {
317
            $img = imagecreate($width, $lines);
318
319
            //画像データの前にパレットデータがあるのでパレットを作成する
320
            $palette_size = $header_size == 12 ? 3 : 4; //OS/2形式の場合は x に相当する箇所のデータは最初から確保されていない
321
            $colors = $clr_used ? $clr_used : pow(2, $bit_count); //色数
322
            $palette = [];
323
            for ($i = 0; $i < $colors; $i++) {
324
                $buf = fread($stream, $palette_size);
325
                if ($buf === false) {
326
                    imagedestroy($img);
327
328
                    return false;
329
                }
330
                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...
331
                $palette[] = imagecolorallocate($img, $r, $g, $b);
332
            }
333
334
            $shift_base = 8 - $bit_count;
335
            $mask = ((1 << $bit_count) - 1) << $shift_base;
336
337
            //圧縮されている場合とされていない場合でデコード処理が大きく変わる
338
            if ($compression == 1 || $compression == 2) {
339
                $x = 0;
340
                $qrt_mod2 = $bit_count >> 2 & 1;
341
                for (; ;) {
342
                    //もし描写先が範囲外になっている場合デコード処理がおかしくなっているので抜ける
343
                    //変なデータが渡されたとしても最悪なケースで255回程度の無駄なので目を瞑る
344
                    if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
345
                        imagedestroy($img);
346
347
                        return false;
348
                    }
349
                    $buf = fread($stream, 1);
350
                    if ($buf === false) {
351
                        imagedestroy($img);
352
353
                        return false;
354
                    }
355
                    switch ($buf) {
356
                    case "\x00":
357
                        $buf = fread($stream, 1);
358
                        if ($buf === false) {
359
                            imagedestroy($img);
360
361
                            return false;
362
                        }
363
                        switch ($buf) {
364
                        case "\x00": //EOL
365
                            $y += $line_step;
366
                            $x = 0;
367
                            break;
368
                        case "\x01": //EOB
369
                            $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...
370
                            $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...
371
                            break 3;
372
                        case "\x02": //MOV
373
                            $buf = fread($stream, 2);
374
                            if ($buf === false) {
375
                                imagedestroy($img);
376
377
                                return false;
378
                            }
379
                            list(, $xx, $yy) = unpack('C2', $buf);
380
                            $x += $xx;
381
                            $y += $yy * $line_step;
382
                            break;
383
                        default:     //ABS
384
                            list(, $pixels) = unpack('C', $buf);
385
                            $bytes = ($pixels >> $qrt_mod2) + ($pixels & $qrt_mod2);
386
                            $buf = fread($stream, ($bytes + 1) & ~1);
387
                            if ($buf === false) {
388
                                imagedestroy($img);
389
390
                                return false;
391
                            }
392
                            for ($i = 0, $pos = 0; $i < $pixels; ++$i, ++$x, $pos += $bit_count) {
393
                                list(, $c) = unpack('C', $buf[$pos >> 3]);
394
                                $b = $pos & 0x07;
395
                                imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
396
                            }
397
                            break;
398
                        }
399
                        break;
400
                    default:
401
                        $buf2 = fread($stream, 1);
402
                        if ($buf2 === false) {
403
                            imagedestroy($img);
404
405
                            return false;
406
                        }
407
                        list(, $size, $c) = unpack('C2', $buf.$buf2);
408 View Code Duplication
                        for ($i = 0, $pos = 0; $i < $size; ++$i, ++$x, $pos += $bit_count) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
409
                            $b = $pos & 0x07;
410
                            imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
411
                        }
412
                        break;
413
                    }
414
                }
415
            } else {
416
                for ($line = 0; $line < $lines; ++$line, $y += $line_step) {
417
                    $buf = fread($stream, $line_bytes);
418
                    if ($buf === false) {
419
                        imagedestroy($img);
420
421
                        return false;
422
                    }
423
424
                    $pos = 0;
425 View Code Duplication
                    for ($x = 0; $x < $width; ++$x, $pos += $bit_count) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
426
                        list(, $c) = unpack('C', $buf[$pos >> 3]);
427
                        $b = $pos & 0x7;
428
                        imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
429
                    }
430
                }
431
            }
432
        } else {
433
            $img = imagecreatetruecolor($width, $lines);
434
            imagealphablending($img, false);
435
            if ($alpha_mask) {
436
                //αデータがあるので透過情報も保存できるように
437
                imagesavealpha($img, true);
438
            }
439
440
            //x軸進行量
441
            $pixel_step = $bit_count >> 3;
442
            $alpha_max = $alpha_mask ? 0x7f : 0x00;
443
            $alpha_mask_r = $alpha_mask ? 1 / $alpha_mask : 1;
444
            $red_mask_r = $red_mask ? 1 / $red_mask : 1;
445
            $green_mask_r = $green_mask ? 1 / $green_mask : 1;
446
            $blue_mask_r = $blue_mask ? 1 / $blue_mask : 1;
447
448
            for ($line = 0; $line < $lines; ++$line, $y += $line_step) {
449
                $buf = fread($stream, $line_bytes);
450
                if ($buf === false) {
451
                    imagedestroy($img);
452
453
                    return false;
454
                }
455
456
                $pos = 0;
457
                for ($x = 0; $x < $width; ++$x, $pos += $pixel_step) {
458
                    list(, $c) = unpack('V', substr($buf, $pos, $pixel_step)."\x00\x00");
459
                    $a_masked = $c & $alpha_mask;
460
                    $r_masked = $c & $red_mask;
461
                    $g_masked = $c & $green_mask;
462
                    $b_masked = $c & $blue_mask;
463
                    $a = $alpha_max - ((($a_masked << 7) - $a_masked) * $alpha_mask_r);
464
                    $r = (($r_masked << 8) - $r_masked) * $red_mask_r;
465
                    $g = (($g_masked << 8) - $g_masked) * $green_mask_r;
466
                    $b = (($b_masked << 8) - $b_masked) * $blue_mask_r;
467
                    imagesetpixel($img, $x, $y, ($a << 24) | ($r << 16) | ($g << 8) | $b);
468
                }
469
            }
470
            imagealphablending($img, true); //デフォルト値に戻しておく
471
        }
472
473
        return $img;
474
    }
475
}
476