Issues (1270)

lib/jimIcon.php (9 issues)

1
<?php
2
3
// Simple .ICO parsing.  The ICO format is insanely complex and this may
4
// fail to correctly handle some technically valid files, but it works
5
// on the majority I've found.
6
//
7
// jimIcon was written in 2013 by Jim Paris <[email protected]> and is
8
// released under the terms of the CC0:
9
//
10
// To the extent possible under law, the author(s) have dedicated all
11
// copyright and related and neighboring rights to this software to
12
// the public domain worldwide. This software is distributed without
13
// any arranty.
14
//
15
// You may have received a copy of the CC0 Public Domain Dedication
16
// along with this software.  If not, see
17
//   http://creativecommons.org/publicdomain/zero/1.0/
18
19
class jimIcon {
20
        // Get an image color from a string
21
        function get_color($str, $img) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
22
                $b = ord($str[0]);
23
                $g = ord($str[1]);
24
                $r = ord($str[2]);
25
                if (strlen($str) > 3) {
26
                        $a = 127 - (ord($str[3]) / 2);
27
                        if ($a != 0 && $a != 127) {
28
                                                        $this->had_alpha = 1;
0 ignored issues
show
Bug Best Practice introduced by
The property had_alpha does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
29
                        }
30
                } else {
31
                        $a = 0;
32
                }
33
                if ($a != 127) {
34
                                        $this->all_transaprent = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property all_transaprent does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
35
                }
36
                return imagecolorallocatealpha($img, $r, $g, $b, $a);
37
        }
38
39
        // Given a string with the contents of an .ICO,
40
        // return a GD image of the icon, or false on error.
41
        function fromiconstring($ico) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
42
                $this->error = "(unknown error)";
0 ignored issues
show
Bug Best Practice introduced by
The property error does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
43
                $this->had_alpha = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property had_alpha does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
44
45
                // Read header
46
                if (strlen($ico) < 6) {
47
                        $this->error = "too short";
48
                        return false;
49
                }
50
                $h = unpack("vzero/vtype/vnum", $ico);
51
52
                // Must be ICO format with at least one image
53
                if ($h["zero"] != 0 || $h["type"] != 1 || $h["num"] == 0) {
54
                        // See if we can just parse it with GD directly
55
                        // if it's not ICO format; maybe it was a mislabeled
56
                        // PNG or something.
57
                        $i = @imagecreatefromstring($ico);
58
                        if ($i) {
0 ignored issues
show
$i is of type false|resource, thus it always evaluated to false.
Loading history...
59
                                imagesavealpha($i, true);
60
                                return $i;
61
                        }
62
                        $this->error = "not ICO or other image";
63
                        return false;
64
                }
65
66
                // Read directory entries to find the biggest image
67
                $most_pixels = 0;
68
                for ($i = 0; $i < $h["num"]; $i++) {
69
                        $entry = substr($ico, 6 + 16 * $i, 16);
70
                        if (!$entry || strlen($entry) < 16) {
71
                                                        continue;
72
                        }
73
                        $e = unpack("Cwidth/".
74
                                    "Cheight/".
75
                                    "Ccolors/".
76
                                    "Czero/".
77
                                    "vplanes/".
78
                                    "vbpp/".
79
                                    "Vsize/".
80
                                    "Voffset/",
81
                                    $entry);
82
                        if ($e["width"] == 0) {
83
                                                        $e["width"] = 256;
84
                        }
85
                        if ($e["height"] == 0) {
86
                                                        $e["height"] = 256;
87
                        }
88
                        if ($e["zero"] != 0) {
89
                                $this->error = "nonzero reserved field";
90
                                return false;
91
                        }
92
                        $pixels = $e["width"] * $e["height"];
93
                        if ($pixels > $most_pixels) {
94
                                $most_pixels = $pixels;
95
                                $most = $e;
96
                        }
97
                }
98
                if ($most_pixels == 0) {
0 ignored issues
show
The condition $most_pixels == 0 is always true.
Loading history...
99
                        $this->error = "no pixels";
100
                        return false;
101
                }
102
                $e = $most;
103
104
                // Extract image data
105
                $data = substr($ico, $e["offset"], $e["size"]);
106
                if (!$data || strlen($data) != $e["size"]) {
107
                        $this->error = "bad image data";
108
                        return false;
109
                }
110
111
                // See if we can parse it (might be PNG format here)
112
                $i = @imagecreatefromstring($data);
113
                if ($i) {
114
                        imagesavealpha($img, true);
115
                        return $i;
116
                }
117
118
                // Must be a BMP.  Parse it ourselves.
119
                $img = imagecreatetruecolor($e["width"], $e["height"]);
120
                imagesavealpha($img, true);
121
                $bg = imagecolorallocatealpha($img, 255, 0, 0, 127);
122
                imagefill($img, 0, 0, $bg);
123
124
                // Skip over the BITMAPCOREHEADER or BITMAPINFOHEADER;
125
                // we'll just assume the palette and pixel data follow
126
                // in the most obvious format as described by the icon
127
                // directory entry.
128
                $bitmapinfo = unpack("Vsize", $data);
129
                if ($bitmapinfo["size"] == 40) {
130
                        $info = unpack("Vsize/".
131
                                        "Vwidth/".
132
                                        "Vheight/".
133
                                        "vplanes/".
134
                                        "vbpp/".
135
                                        "Vcompress/".
136
                                        "Vsize/".
137
                                        "Vxres/".
138
                                        "Vyres/".
139
                                        "Vpalcolors/".
140
                                        "Vimpcolors/", $data);
141
                        if ($e["bpp"] == 0) {
142
                                $e["bpp"] = $info["bpp"];
143
                        }
144
                }
145
                $data = substr($data, $bitmapinfo["size"]);
146
147
                $height = $e["height"];
148
                $width = $e["width"];
149
                $bpp = $e["bpp"];
150
151
                // For indexed images, we only support 1, 4, or 8 BPP
152
                switch ($bpp) {
153
                case 1:
154
                case 4:
155
                case 8:
156
                        $indexed = 1;
157
                        break;
158
                case 24:
159
                case 32:
160
                        $indexed = 0;
161
                        break;
162
                default:
163
                        $this->error = "bad BPP $bpp";
164
                        return false;
165
                }
166
167
                $offset = 0;
168
                if ($indexed) {
169
                        $palette = array();
170
                        $this->all_transparent = 1;
171
                        for ($i = 0; $i < (1 << $bpp); $i++) {
172
                                $entry = substr($data, $i * 4, 4);
173
                                $palette[$i] = $this->get_color($entry, $img);
174
                        }
175
                        $offset = $i * 4;
176
177
                        // Hack for some icons: if everything was transparent,
178
                        // discard alpha channel.
179
                        if ($this->all_transparent) {
180
                                for ($i = 0; $i < (1 << $bpp); $i++) {
181
                                        $palette[$i] &= 0xffffff;
182
                                }
183
                        }
184
                }
185
186
                // Assume image data follows in bottom-up order.
187
                // First the "XOR" image
188
                if ((strlen($data) - $offset) < ($bpp * $height * $width / 8)) {
189
                        $this->error = "short data";
190
                        return false;
191
                }
192
                $XOR = array();
193
                for ($y = $height - 1; $y >= 0; $y--) {
194
                        $x = 0;
195
                        while ($x < $width) {
196
                                if (!$indexed) {
197
                                        $bytes = $bpp / 8;
198
                                        $entry = substr($data, $offset, $bytes);
199
                                        $pixel = $this->get_color($entry, $img);
200
                                        $XOR[$y][$x] = $pixel;
201
                                        $x++;
202
                                        $offset += $bytes;
203
                                } elseif ($bpp == 1) {
204
                                        $p = ord($data[$offset]);
205
                                        for ($b = 0x80; $b > 0; $b >>= 1) {
206
                                                if ($p & $b) {
207
                                                        $pixel = $palette[1];
208
                                                } else {
209
                                                        $pixel = $palette[0];
210
                                                }
211
                                                $XOR[$y][$x] = $pixel;
212
                                                $x++;
213
                                        }
214
                                        $offset++;
215
                                } elseif ($bpp == 4) {
216
                                        $p = ord($data[$offset]);
217
                                        $pixel1 = $palette[$p >> 4];
218
                                        $pixel2 = $palette[$p & 0x0f];
219
                                        $XOR[$y][$x] = $pixel1;
220
                                        $XOR[$y][$x + 1] = $pixel2;
221
                                        $x += 2;
222
                                        $offset++;
223
                                } elseif ($bpp == 8) {
224
                                        $pixel = $palette[ord($data[$offset])];
225
                                        $XOR[$y][$x] = $pixel;
226
                                        $x += 1;
227
                                        $offset++;
228
                                } else {
229
                                        $this->error = "bad BPP";
230
                                        return false;
231
                                }
232
                        }
233
                        // End of row padding
234
                        while ($offset & 3) {
235
                                                        $offset++;
236
                        }
237
                }
238
239
                // Now the "AND" image, which is 1 bit per pixel.  Ignore
240
                // if some of our image data already had alpha values,
241
                // or if there isn't enough data left.
242
                if ($this->had_alpha ||
243
                    ((strlen($data) - $offset) < ($height * $width / 8))) {
244
                        // Just return what we've got
245
                        for ($y = 0; $y < $height; $y++) {
246
                                for ($x = 0; $x < $width; $x++) {
247
                                        imagesetpixel($img, $x, $y,
248
                                                        $XOR[$y][$x]);
249
                                }
250
                        }
251
                        return $img;
252
                }
253
254
                // Mask what we have with the "AND" image
255
                for ($y = $height - 1; $y >= 0; $y--) {
256
                        $x = 0;
257
                        while ($x < $width) {
258
                                for ($b = 0x80;
259
                                        $b > 0 && $x < $width; $b >>= 1) {
260
                                        if (!(ord($data[$offset]) & $b)) {
261
                                                imagesetpixel($img, $x, $y,
262
                                                                $XOR[$y][$x]);
263
                                        }
264
                                        $x++;
265
                                }
266
                                $offset++;
267
                        }
268
269
                        // End of row padding
270
                        while ($offset & 3) {
271
                                                        $offset++;
272
                        }
273
                }
274
                return $img;
275
        }
276
}
277
?>
0 ignored issues
show
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...