1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Elphin\IcoFileLoader; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Open ICO files and extract any size/depth to PNG format. |
7
|
|
|
*/ |
8
|
|
|
class Ico |
9
|
|
|
{ |
10
|
|
|
/** |
11
|
|
|
* Background color on icon extraction. |
12
|
|
|
* @var array(R, G, B) = array(255, 255, 255) |
13
|
|
|
*/ |
14
|
|
|
public $bgcolor = [255, 255, 255]; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @var bool Is background color transparent? |
18
|
|
|
*/ |
19
|
|
|
public $bgcolorTransparent = false; |
20
|
|
|
|
21
|
|
|
private $filename; |
22
|
|
|
private $ico; |
23
|
|
|
private $iconDirEntry; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Constructor |
27
|
|
|
* |
28
|
|
|
* @param string $path optional path to ICO file |
29
|
|
|
*/ |
30
|
6 |
|
public function __construct($path = '') |
31
|
|
|
{ |
32
|
6 |
|
if (strlen($path) > 0) { |
33
|
5 |
|
$this->loadFile($path); |
34
|
5 |
|
} |
35
|
6 |
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Load an ICO file (don't need to call this is if fill the |
39
|
|
|
* parameter in the class constructor). |
40
|
|
|
* |
41
|
|
|
* @param string $path Path to ICO file |
42
|
|
|
* |
43
|
|
|
* @return bool Success |
44
|
|
|
*/ |
45
|
6 |
|
public function loadFile($path) |
46
|
|
|
{ |
47
|
6 |
|
$this->filename = $path; |
48
|
6 |
|
if (($fp = @fopen($path, 'rb')) !== false) { |
49
|
6 |
|
$data = ''; |
50
|
6 |
|
while (!feof($fp)) { |
51
|
6 |
|
$data .= fread($fp, 4096); |
52
|
6 |
|
} |
53
|
6 |
|
fclose($fp); |
54
|
|
|
|
55
|
6 |
|
return $this->loadData($data); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
return false; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Load an ICO data. If you prefer to open the file |
63
|
|
|
* and return the binary data you can use this function |
64
|
|
|
* directly. Otherwise use loadFile() instead. |
65
|
|
|
* |
66
|
|
|
* @param string $data Binary data of ICO file |
67
|
|
|
* |
68
|
|
|
* @return bool Success |
69
|
|
|
*/ |
70
|
6 |
|
private function loadData($data) |
71
|
|
|
{ |
72
|
6 |
|
$this->iconDirEntry = []; |
73
|
|
|
|
74
|
|
|
//extract ICONDIR header |
75
|
6 |
|
$icodata = unpack('SReserved/SType/SCount', $data); |
76
|
6 |
|
$this->ico = $icodata; |
77
|
6 |
|
$data = substr($data, 6); |
78
|
|
|
|
79
|
|
|
//extract ICONDIRENTRY structures |
80
|
6 |
|
$data = $this->extractIconDirEntries($data); |
81
|
|
|
|
82
|
|
|
// Extract additional headers for each extracted icon header |
83
|
6 |
|
$iconCount = count($this->iconDirEntry); |
84
|
6 |
|
for ($i = 0; $i < $iconCount; ++$i) { |
85
|
6 |
|
$bitmapInfoHeader = unpack( |
86
|
|
|
'LSize/LWidth/LHeight/SPlanes/SBitCount/LCompression/LImageSize/' . |
87
|
6 |
|
'LXpixelsPerM/LYpixelsPerM/LColorsUsed/LColorsImportant', |
88
|
6 |
|
substr($data, $this->iconDirEntry[$i]['FileOffset']) |
89
|
6 |
|
); |
90
|
|
|
|
91
|
6 |
|
$this->iconDirEntry[$i]['header'] = $bitmapInfoHeader; |
92
|
6 |
|
$this->iconDirEntry[$i]['colors'] = []; |
93
|
6 |
|
$this->iconDirEntry[$i]['BitCount'] = $this->iconDirEntry[$i]['header']['BitCount']; |
94
|
|
|
|
95
|
6 |
|
switch ($this->iconDirEntry[$i]['BitCount']) { |
96
|
6 |
|
case 32: |
97
|
6 |
|
case 24: |
98
|
3 |
|
$this->extract24BitData($i, $data); |
99
|
3 |
|
break; |
100
|
3 |
|
case 8: |
101
|
3 |
|
case 4: |
102
|
2 |
|
$this->extract8BitData($i, $data); |
103
|
2 |
|
break; |
104
|
1 |
|
case 1: |
105
|
1 |
|
$this->extract1BitData($i, $data); |
106
|
1 |
|
break; |
107
|
6 |
|
} |
108
|
6 |
|
$this->iconDirEntry[$i]['data_length'] = strlen($this->iconDirEntry[$i]['data']); |
109
|
6 |
|
} |
110
|
6 |
|
return true; |
111
|
|
|
} |
112
|
|
|
|
113
|
3 |
|
private function extract24BitData($i, $data) |
114
|
|
|
{ |
115
|
3 |
|
$length = $this->iconDirEntry[$i]['header']['Width'] * |
116
|
3 |
|
$this->iconDirEntry[$i]['header']['Height'] * |
117
|
3 |
|
($this->iconDirEntry[$i]['BitCount'] / 8); |
118
|
3 |
|
$this->iconDirEntry[$i]['data'] = substr( |
119
|
3 |
|
$data, |
120
|
3 |
|
$this->iconDirEntry[$i]['FileOffset'] + $this->iconDirEntry[$i]['header']['Size'], |
121
|
|
|
$length |
122
|
3 |
|
); |
123
|
3 |
|
} |
124
|
|
|
|
125
|
2 |
|
private function extract8BitData($i, $data) |
126
|
|
|
{ |
127
|
2 |
|
$icodata = substr( |
128
|
2 |
|
$data, |
129
|
2 |
|
$this->iconDirEntry[$i]['FileOffset'] + $this->iconDirEntry[$i]['header']['Size'], |
130
|
2 |
|
$this->iconDirEntry[$i]['ColorCount'] * 4 |
131
|
2 |
|
); |
132
|
2 |
|
$offset = 0; |
133
|
2 |
|
for ($j = 0; $j < $this->iconDirEntry[$i]['ColorCount']; ++$j) { |
134
|
2 |
|
$this->iconDirEntry[$i]['colors'][] = [ |
135
|
2 |
|
'blue' => ord($icodata[$offset]), |
136
|
2 |
|
'green' => ord($icodata[$offset + 1]), |
137
|
2 |
|
'red' => ord($icodata[$offset + 2]), |
138
|
2 |
|
'reserved' => ord($icodata[$offset + 3]), |
139
|
|
|
]; |
140
|
2 |
|
$offset += 4; |
141
|
2 |
|
} |
142
|
2 |
|
$length = $this->iconDirEntry[$i]['header']['Width'] * |
143
|
2 |
|
$this->iconDirEntry[$i]['header']['Height'] * |
144
|
2 |
|
(1 + $this->iconDirEntry[$i]['BitCount']) / $this->iconDirEntry[$i]['BitCount']; |
145
|
2 |
|
$this->iconDirEntry[$i]['data'] = substr( |
146
|
2 |
|
$data, |
147
|
2 |
|
$this->iconDirEntry[$i]['FileOffset'] + |
148
|
2 |
|
($this->iconDirEntry[$i]['ColorCount'] * 4) + |
149
|
2 |
|
$this->iconDirEntry[$i]['header']['Size'], |
150
|
|
|
$length |
151
|
2 |
|
); |
152
|
2 |
|
} |
153
|
|
|
|
154
|
1 |
|
private function extract1BitData($i, $data) |
155
|
|
|
{ |
156
|
1 |
|
$icodata = substr( |
157
|
1 |
|
$data, |
158
|
1 |
|
$this->iconDirEntry[$i]['FileOffset'] + $this->iconDirEntry[$i]['header']['Size'], |
159
|
1 |
|
$this->iconDirEntry[$i]['ColorCount'] * 4 |
160
|
1 |
|
); |
161
|
|
|
|
162
|
1 |
|
$this->iconDirEntry[$i]['colors'][] = [ |
163
|
1 |
|
'blue' => ord($icodata[0]), |
164
|
1 |
|
'green' => ord($icodata[1]), |
165
|
1 |
|
'red' => ord($icodata[2]), |
166
|
1 |
|
'reserved' => ord($icodata[3]), |
167
|
|
|
]; |
168
|
1 |
|
$this->iconDirEntry[$i]['colors'][] = [ |
169
|
1 |
|
'blue' => ord($icodata[4]), |
170
|
1 |
|
'green' => ord($icodata[5]), |
171
|
1 |
|
'red' => ord($icodata[6]), |
172
|
1 |
|
'reserved' => ord($icodata[7]), |
173
|
|
|
]; |
174
|
|
|
|
175
|
1 |
|
$length = $this->iconDirEntry[$i]['header']['Width'] * $this->iconDirEntry[$i]['header']['Height'] / 8; |
176
|
1 |
|
$this->iconDirEntry[$i]['data'] = substr( |
177
|
1 |
|
$data, |
178
|
1 |
|
$this->iconDirEntry[$i]['FileOffset'] + $this->iconDirEntry[$i]['header']['Size'] + 8, |
179
|
|
|
$length |
180
|
1 |
|
); |
181
|
1 |
|
} |
182
|
|
|
|
183
|
6 |
|
private function extractIconDirEntries($data) |
184
|
|
|
{ |
185
|
6 |
|
for ($i = 0; $i < $this->ico['Count']; ++$i) { |
186
|
6 |
|
$icodata = unpack('CWidth/CHeight/CColorCount/CReserved/SPlanes/SBitCount/LSizeInBytes/LFileOffset', $data); |
187
|
6 |
|
$icodata['FileOffset'] -= ($this->ico['Count'] * 16) + 6; |
188
|
6 |
|
if ($icodata['ColorCount'] == 0) { |
189
|
4 |
|
$icodata['ColorCount'] = 256; |
190
|
4 |
|
} |
191
|
6 |
|
$this->iconDirEntry[] = $icodata; |
192
|
|
|
|
193
|
6 |
|
$data = substr($data, 16); |
194
|
6 |
|
} |
195
|
|
|
|
196
|
6 |
|
return $data; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Return the total icons extracted at the moment. |
201
|
|
|
* |
202
|
|
|
* @return int Total icons |
203
|
|
|
*/ |
204
|
6 |
|
public function getTotalIcons() |
205
|
|
|
{ |
206
|
6 |
|
return count($this->iconDirEntry); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* Return the icon header corresponding to that index. |
211
|
|
|
* |
212
|
|
|
* @param int $index Icon index |
213
|
|
|
* |
214
|
|
|
* @return resource|bool Icon header or false |
215
|
|
|
*/ |
216
|
6 |
|
public function getIconInfo($index) |
217
|
|
|
{ |
218
|
6 |
|
if (isset($this->iconDirEntry[$index])) { |
219
|
6 |
|
return $this->iconDirEntry[$index]; |
220
|
|
|
} |
221
|
|
|
|
222
|
1 |
|
return false; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Changes background color of extraction. You can set |
227
|
|
|
* the 3 color components or set $red = '#xxxxxx' (HTML format) |
228
|
|
|
* and leave all other blanks. |
229
|
|
|
* |
230
|
|
|
* @param int $red Red component |
231
|
|
|
* @param int $green Green component |
232
|
|
|
* @param int $blue Blue component |
233
|
|
|
*/ |
234
|
5 |
|
public function setBackground($red = 255, $green = 255, $blue = 255) |
235
|
|
|
{ |
236
|
5 |
|
if (is_string($red) && preg_match('/^\#[0-9a-f]{6}$/', $red)) { |
237
|
5 |
|
$green = hexdec($red[3] . $red[4]); |
238
|
5 |
|
$blue = hexdec($red[5] . $red[6]); |
239
|
5 |
|
$red = hexdec($red[1] . $red[2]); |
240
|
5 |
|
} |
241
|
|
|
|
242
|
5 |
|
$this->bgcolor = [$red, $green, $blue]; |
243
|
5 |
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Set background color to be saved as transparent. |
247
|
|
|
* |
248
|
|
|
* @param bool $transparent Is Transparent or not |
249
|
|
|
* |
250
|
|
|
* @return bool Is Transparent or not |
251
|
|
|
*/ |
252
|
1 |
|
public function setBackgroundTransparent($transparent = true) |
253
|
|
|
{ |
254
|
1 |
|
return $this->bgcolorTransparent = $transparent; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Return an image resource with the icon stored |
259
|
|
|
* on the $index position of the ICO file. |
260
|
|
|
* |
261
|
|
|
* @param int $index Position of the icon inside ICO |
262
|
|
|
* |
263
|
|
|
* @return resource|bool Image resource |
264
|
|
|
**/ |
265
|
6 |
|
public function getImage($index) |
266
|
|
|
{ |
267
|
6 |
|
if (!isset($this->iconDirEntry[$index])) { |
268
|
|
|
return false; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
// create image filled with desired background color |
272
|
6 |
|
$im = imagecreatetruecolor($this->iconDirEntry[$index]['Width'], $this->iconDirEntry[$index]['Height']); |
273
|
6 |
|
$bgcolor = $this->allocateColor($im, $this->bgcolor[0], $this->bgcolor[1], $this->bgcolor[2]); |
274
|
6 |
|
imagefilledrectangle( |
275
|
6 |
|
$im, |
276
|
6 |
|
0, |
277
|
6 |
|
0, |
278
|
6 |
|
$this->iconDirEntry[$index]['Width'], |
279
|
6 |
|
$this->iconDirEntry[$index]['Height'], |
280
|
|
|
$bgcolor |
281
|
6 |
|
); |
282
|
|
|
|
283
|
6 |
|
if ($this->bgcolorTransparent) { |
284
|
1 |
|
imagecolortransparent($im, $bgcolor); |
285
|
1 |
|
} |
286
|
|
|
|
287
|
|
|
// now paint pixels based on bit count |
288
|
6 |
|
switch ($this->iconDirEntry[$index]['BitCount']) { |
289
|
6 |
|
case 32: |
290
|
2 |
|
$this->render32bit($this->iconDirEntry[$index], $im); |
291
|
2 |
|
break; |
292
|
4 |
|
case 24: |
293
|
1 |
|
$this->render24bit($this->iconDirEntry[$index], $im); |
294
|
1 |
|
break; |
295
|
3 |
|
case 8: |
296
|
1 |
|
$this->render8bit($this->iconDirEntry[$index], $im); |
297
|
1 |
|
break; |
298
|
2 |
|
case 4: |
299
|
1 |
|
$this->render4bit($this->iconDirEntry[$index], $im); |
300
|
1 |
|
break; |
301
|
1 |
|
case 1: |
302
|
1 |
|
$this->render1bit($this->iconDirEntry[$index], $im); |
303
|
1 |
|
break; |
304
|
6 |
|
} |
305
|
|
|
|
306
|
6 |
|
return $im; |
307
|
|
|
} |
308
|
|
|
|
309
|
2 |
|
private function render32bit($metadata, $im) |
310
|
|
|
{ |
311
|
|
|
/** |
312
|
|
|
* 32 bits: 4 bytes per pixel [ B | G | R | ALPHA ]. |
313
|
|
|
**/ |
314
|
2 |
|
$offset = 0; |
315
|
2 |
|
$binary = $metadata['data']; |
316
|
|
|
|
317
|
2 |
|
for ($i = $metadata['Height'] - 1; $i >= 0; --$i) { |
318
|
2 |
|
for ($j = 0; $j < $metadata['Width']; ++$j) { |
319
|
|
|
//we translate the BGRA to aRGB ourselves, which is twice as fast |
320
|
|
|
//as calling imagecolorallocatealpha |
321
|
2 |
|
$alpha7 = ((~ord($binary[$offset + 3])) & 0xff) >> 1; |
322
|
2 |
View Code Duplication |
if ($alpha7 < 127) { |
|
|
|
|
323
|
2 |
|
$col = ($alpha7 << 24) | |
324
|
2 |
|
(ord($binary[$offset + 2]) << 16) | |
325
|
2 |
|
(ord($binary[$offset + 1]) << 8) | |
326
|
2 |
|
(ord($binary[$offset])); |
327
|
2 |
|
imagesetpixel($im, $j, $i, $col); |
328
|
2 |
|
} |
329
|
2 |
|
$offset += 4; |
330
|
2 |
|
} |
331
|
2 |
|
} |
332
|
2 |
|
} |
333
|
|
|
|
334
|
1 |
|
private function render24bit($metadata, $im) |
335
|
|
|
{ |
336
|
1 |
|
$maskBits = $this->buildMaskBits($metadata); |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* 24 bits: 3 bytes per pixel [ B | G | R ]. |
340
|
|
|
**/ |
341
|
1 |
|
$offset = 0; |
342
|
1 |
|
$bitoffset = 0; |
343
|
1 |
|
$binary = $metadata['data']; |
344
|
|
|
|
345
|
1 |
|
for ($i = $metadata['Height'] - 1; $i >= 0; --$i) { |
346
|
1 |
|
for ($j = 0; $j < $metadata['Width']; ++$j) { |
347
|
1 |
View Code Duplication |
if ($maskBits[$bitoffset] == 0) { |
|
|
|
|
348
|
|
|
//translate BGR to RGB |
349
|
1 |
|
$col = (ord($binary[$offset + 2]) << 16) | |
350
|
1 |
|
(ord($binary[$offset + 1]) << 8) | |
351
|
1 |
|
(ord($binary[$offset])); |
352
|
1 |
|
imagesetpixel($im, $j, $i, $col); |
353
|
1 |
|
} |
354
|
1 |
|
$offset += 3; |
355
|
1 |
|
++$bitoffset; |
356
|
1 |
|
} |
357
|
1 |
|
} |
358
|
1 |
|
} |
359
|
|
|
|
360
|
1 |
|
private function render8bit($metadata, $im) |
361
|
|
|
{ |
362
|
1 |
|
$palette = $this->buildPalette($metadata, $im); |
363
|
1 |
|
$maskBits = $this->buildMaskBits($metadata); |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* 8 bits: 1 byte per pixel [ COLOR INDEX ]. |
367
|
|
|
**/ |
368
|
1 |
|
$offset = 0; |
369
|
1 |
View Code Duplication |
for ($i = $metadata['Height'] - 1; $i >= 0; --$i) { |
|
|
|
|
370
|
1 |
|
for ($j = 0; $j < $metadata['Width']; ++$j) { |
371
|
1 |
|
if ($maskBits[$offset] == 0) { |
372
|
1 |
|
$color = ord($metadata['data'][$offset]); |
373
|
1 |
|
imagesetpixel($im, $j, $i, $palette[$color]); |
374
|
1 |
|
} |
375
|
1 |
|
++$offset; |
376
|
1 |
|
} |
377
|
1 |
|
} |
378
|
1 |
|
} |
379
|
|
|
|
380
|
1 |
|
private function render4bit($metadata, $im) |
381
|
|
|
{ |
382
|
1 |
|
$palette = $this->buildPalette($metadata, $im); |
383
|
1 |
|
$maskBits = $this->buildMaskBits($metadata); |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* 4 bits: half byte/nibble per pixel [ COLOR INDEX ]. |
387
|
|
|
**/ |
388
|
1 |
|
$offset = 0; |
389
|
1 |
|
$maskoffset = 0; |
390
|
1 |
|
for ($i = $metadata['Height'] - 1; $i >= 0; --$i) { |
391
|
1 |
|
for ($j = 0; $j < $metadata['Width']; $j += 2) { |
392
|
1 |
|
$colorByte = ord($metadata['data'][$offset]); |
393
|
1 |
|
$lowNibble = $colorByte & 0x0f; |
394
|
1 |
|
$highNibble = ($colorByte & 0xf0) >> 4; |
395
|
|
|
|
396
|
1 |
|
if ($maskBits[$maskoffset++] == 0) { |
397
|
1 |
|
imagesetpixel($im, $j, $i, $palette[$highNibble]); |
398
|
1 |
|
} |
399
|
|
|
|
400
|
1 |
|
if ($maskBits[$maskoffset++] == 0) { |
401
|
1 |
|
imagesetpixel($im, $j + 1, $i, $palette[$lowNibble]); |
402
|
1 |
|
} |
403
|
1 |
|
$offset++; |
404
|
1 |
|
} |
405
|
1 |
|
} |
406
|
1 |
|
} |
407
|
|
|
|
408
|
1 |
|
private function render1bit($metadata, $im) |
409
|
|
|
{ |
410
|
1 |
|
$palette = $this->buildPalette($metadata, $im); |
411
|
1 |
|
$maskBits = $this->buildMaskBits($metadata); |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* 1 bit: 1 bit per pixel (2 colors, usually black&white) [ COLOR INDEX ]. |
415
|
|
|
**/ |
416
|
1 |
|
$colorbits = ''; |
417
|
1 |
|
$total = strlen($metadata['data']); |
418
|
1 |
|
for ($i = 0; $i < $total; ++$i) { |
419
|
1 |
|
$colorbits .= str_pad(decbin(ord($metadata['data'][$i])), 8, '0', STR_PAD_LEFT); |
420
|
1 |
|
} |
421
|
|
|
|
422
|
1 |
|
$offset = 0; |
423
|
1 |
View Code Duplication |
for ($i = $metadata['Height'] - 1; $i >= 0; --$i) { |
|
|
|
|
424
|
1 |
|
for ($j = 0; $j < $metadata['Width']; ++$j) { |
425
|
1 |
|
if ($maskBits[$offset] == 0) { |
426
|
1 |
|
imagesetpixel($im, $j, $i, $palette[$colorbits[$offset]]); |
427
|
1 |
|
} |
428
|
1 |
|
++$offset; |
429
|
1 |
|
} |
430
|
1 |
|
} |
431
|
1 |
|
} |
432
|
|
|
|
433
|
3 |
|
private function buildPalette($metadata, $im) |
434
|
|
|
{ |
435
|
3 |
|
$palette = []; |
436
|
3 |
|
if ($metadata['BitCount'] != 24) { |
437
|
3 |
|
$palette = []; |
438
|
3 |
|
for ($i = 0; $i < $metadata['ColorCount']; ++$i) { |
439
|
3 |
|
$palette[$i] = $this->allocateColor( |
440
|
3 |
|
$im, |
441
|
3 |
|
$metadata['colors'][$i]['red'], |
442
|
3 |
|
$metadata['colors'][$i]['green'], |
443
|
3 |
|
$metadata['colors'][$i]['blue'], |
444
|
3 |
|
round($metadata['colors'][$i]['reserved'] / 255 * 127) |
445
|
3 |
|
); |
446
|
3 |
|
} |
447
|
3 |
|
} |
448
|
3 |
|
return $palette; |
449
|
|
|
} |
450
|
|
|
|
451
|
4 |
|
private function buildMaskBits($metadata) |
452
|
|
|
{ |
453
|
4 |
|
$width = $metadata['Width']; |
454
|
4 |
|
if (($width % 32) > 0) { |
455
|
|
|
$width += (32 - ($metadata['Width'] % 32)); |
456
|
|
|
} |
457
|
4 |
|
$offset = $metadata['Width'] * |
458
|
4 |
|
$metadata['Height'] * |
459
|
4 |
|
$metadata['BitCount'] / 8; |
460
|
4 |
|
$total_bytes = ($width * $metadata['Height']) / 8; |
461
|
4 |
|
$maskBits = ''; |
462
|
4 |
|
$bytes = 0; |
463
|
4 |
|
$bytes_per_line = ($metadata['Width'] / 8); |
464
|
4 |
|
$bytes_to_remove = (($width - $metadata['Width']) / 8); |
465
|
4 |
|
for ($i = 0; $i < $total_bytes; ++$i) { |
466
|
4 |
|
$maskBits .= str_pad(decbin(ord($metadata['data'][$offset + $i])), 8, '0', STR_PAD_LEFT); |
467
|
4 |
|
++$bytes; |
468
|
4 |
|
if ($bytes == $bytes_per_line) { |
469
|
4 |
|
$i += $bytes_to_remove; |
470
|
4 |
|
$bytes = 0; |
471
|
4 |
|
} |
472
|
4 |
|
} |
473
|
4 |
|
return $maskBits; |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
/** |
477
|
|
|
* Allocate a color on $im resource. This function prevents |
478
|
|
|
* from allocating same colors on the same pallete. Instead |
479
|
|
|
* if it finds that the color is already allocated, it only |
480
|
|
|
* returns the index to that color. |
481
|
|
|
* It supports alpha channel. |
482
|
|
|
* |
483
|
|
|
* @param resource $im Image resource |
484
|
|
|
* @param int $red Red component |
485
|
|
|
* @param int $green Green component |
486
|
|
|
* @param int $blue Blue component |
487
|
|
|
* @param int $alpha Alpha channel |
488
|
|
|
* |
489
|
|
|
* @return int Color index |
490
|
|
|
*/ |
491
|
6 |
|
private function allocateColor(&$im, $red, $green, $blue, $alpha = 0) |
492
|
|
|
{ |
493
|
6 |
|
$c = imagecolorexactalpha($im, $red, $green, $blue, $alpha); |
494
|
6 |
|
if ($c >= 0) { |
495
|
6 |
|
return $c; |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
return imagecolorallocatealpha($im, $red, $green, $blue, $alpha); |
499
|
|
|
} |
500
|
|
|
} |
501
|
|
|
|
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.