Completed
Push — master ( 312aa1...eb49de )
by Paul
9s
created

IcoParser::parseMonoImageData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 4
Ratio 28.57 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
dl 4
loc 14
ccs 11
cts 11
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 2
crap 2
1
<?php
2
3
namespace Elphin\IcoFileLoader;
4
5
/**
6
 * IcoParser provides the means to read an ico file and produce an Icon object
7
 * containing many IconImage objects
8
 *
9
 * @package Elphin\IcoFileLoader
10
 */
11
class IcoParser implements ParserInterface
12
{
13
    /**
14
     * @inheritdoc
15
     */
16 5
    public function isSupportedBinaryString($data)
17
    {
18 5
        return !is_null($this->parseIconDir($data));
19
    }
20
21
    /**
22
     * Reads the ICONDIR header and verifies it looks sane
23
     * @param string $data
24
     * @return array|null - null is returned if the file doesn't look like an .ico file
25
     */
26 17
    private function parseIconDir($data)
27
    {
28 17
        $icondir = unpack('SReserved/SType/SCount', $data);
29 17
        if ($icondir['Reserved'] == 0 && $icondir['Type'] == 1) {
30 15
            return $icondir;
31
        }
32 5
        return null;
33
    }
34
35
    /**
36
     * @inheritdoc
37
     */
38 16
    public function parse($data)
39
    {
40 16
        $icondir = $this->parseIconDir($data);
41 16
        if (!$icondir) {
42 1
            throw new \InvalidArgumentException('Invalid ICO file format');
43
        }
44
45
        //nibble the header off our data
46 15
        $data = substr($data, 6);
47
48
        //parse the ICONDIRENTRY headers
49 15
        $icon = new Icon();
50 15
        $data = $this->parseIconDirEntries($icon, $data, $icondir['Count']);
51
52
        // Extract additional headers for each extracted ICONDIRENTRY
53 15
        for ($i = 0; $i < count($icon); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
54 15
            $signature = unpack('LFourCC', substr($data, $icon[$i]->fileOffset, 4));
55 15
            if ($signature['FourCC'] == 0x474e5089) {
56 2
                $this->parsePng($icon[$i], $data);
57 2
            } else {
58 15
                $this->parseBmp($icon[$i], $data);
59
            }
60 15
        }
61
62 15
        return $icon;
63
    }
64
65 15
    private function parseIconDirEntries(Icon $icon, $data, $count)
66
    {
67 15
        for ($i = 0; $i < $count; ++$i) {
68 15
            $icoDirEntry = unpack(
69 15
                'Cwidth/Cheight/CcolorCount/Creserved/Splanes/SbitCount/LsizeInBytes/LfileOffset',
70
                $data
71 15
            );
72 15
            $icoDirEntry['fileOffset'] -= ($count * 16) + 6;
73 15
            if ($icoDirEntry['colorCount'] == 0) {
74 11
                $icoDirEntry['colorCount'] = 256;
75 11
            }
76 15
            if ($icoDirEntry['width'] == 0) {
77 2
                $icoDirEntry['width'] = 256;
78 2
            }
79 15
            if ($icoDirEntry['height'] == 0) {
80 2
                $icoDirEntry['height'] = 256;
81 2
            }
82
83 15
            $entry = new IconImage($icoDirEntry);
84 15
            $icon[] = $entry;
85
86 15
            $data = substr($data, 16);
87 15
        }
88
89 15
        return $data;
90
    }
91
92 2
    private function parsePng(IconImage $entry, $data)
93
    {
94
        //a png icon contains a complete png image at the file offset
95 2
        $png = substr($data, $entry->fileOffset, $entry->sizeInBytes);
96 2
        $entry->setPngFile($png);
97 2
    }
98
99 15
    private function parseBmp(IconImage $entry, $data)
100
    {
101 15
        $bitmapInfoHeader = unpack(
102
            'LSize/LWidth/LHeight/SPlanes/SBitCount/LCompression/LImageSize/' .
103 15
            'LXpixelsPerM/LYpixelsPerM/LColorsUsed/LColorsImportant',
104 15
            substr($data, $entry->fileOffset, 40)
105 15
        );
106
107 15
        $entry->setBitmapInfoHeader($bitmapInfoHeader);
108
109 15
        switch ($entry->bitCount) {
110 15
            case 32:
111 15
            case 24:
112 9
                $this->parseTrueColorImageData($entry, $data);
113 9
                break;
114 8
            case 8:
115 8
            case 4:
116 6
                $this->parsePaletteImageData($entry, $data);
117 6
                break;
118 2
            case 1:
119 2
                $this->parseMonoImageData($entry, $data);
120 2
                break;
121 15
        }
122 15
    }
123
124 9
    private function parseTrueColorImageData(IconImage $entry, $data)
125
    {
126 9
        $length = $entry->bmpHeaderWidth * $entry->bmpHeaderHeight * ($entry->bitCount / 8);
127 9
        $bmpData = substr($data, $entry->fileOffset + $entry->bmpHeaderSize, $length);
128 9
        $entry->setBitmapData($bmpData);
129 9
    }
130
131 6
    private function parsePaletteImageData(IconImage $entry, $data)
132
    {
133
        //var_dump($entry->colorCount);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
134
135 6
        $pal = substr($data, $entry->fileOffset + $entry->bmpHeaderSize, $entry->colorCount * 4);
136 6
        $idx = 0;
137 6 View Code Duplication
        for ($j = 0; $j < $entry->colorCount; ++$j) {
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...
138 6
            $entry->addToBmpPalette(ord($pal[$idx + 2]), ord($pal[$idx + 1]), ord($pal[$idx]), ord($pal[$idx + 3]));
139 6
            $idx += 4;
140 6
        }
141
142 6
        $length = $entry->bmpHeaderWidth * $entry->bmpHeaderHeight * (1 + $entry->bitCount) / $entry->bitCount;
143 6
        $bmpData = substr($data, $entry->fileOffset + $entry->bmpHeaderSize + $entry->colorCount * 4, $length);
144 6
        $entry->setBitmapData($bmpData);
145 6
    }
146
147 2
    private function parseMonoImageData(IconImage $entry, $data)
148
    {
149 2
        $pal = substr($data, $entry->fileOffset + $entry->bmpHeaderSize, $entry->colorCount * 4);
150
151 2
        $idx = 0;
152 2 View Code Duplication
        for ($j = 0; $j < $entry->colorCount; ++$j) {
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...
153 2
            $entry->addToBmpPalette(ord($pal[$idx + 2]), ord($pal[$idx + 1]), ord($pal[$idx]), ord($pal[$idx + 3]));
154 2
            $idx += 4;
155 2
        }
156
157 2
        $length = $entry->bmpHeaderWidth * $entry->bmpHeaderHeight / 8;
158 2
        $bmpData = substr($data, $entry->fileOffset + $entry->bmpHeaderSize + $entry->colorCount * 4, $length);
159 2
        $entry->setBitmapData($bmpData);
160 2
    }
161
}
162