Chris@76
|
1 <?php
|
Chris@76
|
2
|
Chris@76
|
3 /**
|
Chris@76
|
4 * Simple Machines Forum (SMF)
|
Chris@76
|
5 *
|
Chris@76
|
6 * @package SMF
|
Chris@76
|
7 * @author Simple Machines http://www.simplemachines.org
|
Chris@76
|
8 * @copyright 2011 Simple Machines
|
Chris@76
|
9 * @license http://www.simplemachines.org/about/smf/license.php BSD
|
Chris@76
|
10 *
|
Chris@76
|
11 * @version 2.0
|
Chris@76
|
12 */
|
Chris@76
|
13
|
Chris@76
|
14 /* Gif Util copyright 2003 by Yamasoft (S/C). All rights reserved.
|
Chris@76
|
15 Do not remove this portion of the header, or use these functions except
|
Chris@76
|
16 from the original author. To get it, please navigate to:
|
Chris@76
|
17 http://www.yamasoft.com/php-gif.zip
|
Chris@76
|
18 */
|
Chris@76
|
19
|
Chris@76
|
20 if (!defined('SMF'))
|
Chris@76
|
21 die('Hacking attempt...');
|
Chris@76
|
22
|
Chris@76
|
23 /* Classes used for reading gif files (in case PHP's GD doesn't provide the
|
Chris@76
|
24 proper gif-functions).
|
Chris@76
|
25 */
|
Chris@76
|
26
|
Chris@76
|
27 class gif_lzw_compression
|
Chris@76
|
28 {
|
Chris@76
|
29 public $MAX_LZW_BITS;
|
Chris@76
|
30 public $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode;
|
Chris@76
|
31 public $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte;
|
Chris@76
|
32
|
Chris@76
|
33 public function __construct()
|
Chris@76
|
34 {
|
Chris@76
|
35 $this->MAX_LZW_BITS = 12;
|
Chris@76
|
36 unset($this->Next, $this->Vals, $this->Stack, $this->Buf);
|
Chris@76
|
37
|
Chris@76
|
38 $this->Next = range(0, (1 << $this->MAX_LZW_BITS) - 1);
|
Chris@76
|
39 $this->Vals = range(0, (1 << $this->MAX_LZW_BITS) - 1);
|
Chris@76
|
40 $this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
|
Chris@76
|
41 $this->Buf = range(0, 279);
|
Chris@76
|
42 }
|
Chris@76
|
43
|
Chris@76
|
44 public function decompress($data, &$datLen)
|
Chris@76
|
45 {
|
Chris@76
|
46 $stLen = strlen($data);
|
Chris@76
|
47 $datLen = 0;
|
Chris@76
|
48 $ret = '';
|
Chris@76
|
49
|
Chris@76
|
50 $this->LZWCommand($data, true);
|
Chris@76
|
51
|
Chris@76
|
52 while (($iIndex = $this->LZWCommand($data, false)) >= 0)
|
Chris@76
|
53 $ret .= chr($iIndex);
|
Chris@76
|
54
|
Chris@76
|
55 $datLen = $stLen - strlen($data);
|
Chris@76
|
56
|
Chris@76
|
57 if ($iIndex != -2)
|
Chris@76
|
58 return false;
|
Chris@76
|
59
|
Chris@76
|
60 return $ret;
|
Chris@76
|
61 }
|
Chris@76
|
62
|
Chris@76
|
63 public function LZWCommand(&$data, $bInit)
|
Chris@76
|
64 {
|
Chris@76
|
65 if ($bInit)
|
Chris@76
|
66 {
|
Chris@76
|
67 $this->SetCodeSize = ord($data[0]);
|
Chris@76
|
68 $data = substr($data, 1);
|
Chris@76
|
69
|
Chris@76
|
70 $this->CodeSize = $this->SetCodeSize + 1;
|
Chris@76
|
71 $this->ClearCode = 1 << $this->SetCodeSize;
|
Chris@76
|
72 $this->EndCode = $this->ClearCode + 1;
|
Chris@76
|
73 $this->MaxCode = $this->ClearCode + 2;
|
Chris@76
|
74 $this->MaxCodeSize = $this->ClearCode << 1;
|
Chris@76
|
75
|
Chris@76
|
76 $this->GetCode($data, $bInit);
|
Chris@76
|
77
|
Chris@76
|
78 $this->Fresh = 1;
|
Chris@76
|
79 for ($i = 0; $i < $this->ClearCode; $i++)
|
Chris@76
|
80 {
|
Chris@76
|
81 $this->Next[$i] = 0;
|
Chris@76
|
82 $this->Vals[$i] = $i;
|
Chris@76
|
83 }
|
Chris@76
|
84
|
Chris@76
|
85 for (; $i < (1 << $this->MAX_LZW_BITS); $i++)
|
Chris@76
|
86 {
|
Chris@76
|
87 $this->Next[$i] = 0;
|
Chris@76
|
88 $this->Vals[$i] = 0;
|
Chris@76
|
89 }
|
Chris@76
|
90
|
Chris@76
|
91 $this->sp = 0;
|
Chris@76
|
92 return 1;
|
Chris@76
|
93 }
|
Chris@76
|
94
|
Chris@76
|
95 if ($this->Fresh)
|
Chris@76
|
96 {
|
Chris@76
|
97 $this->Fresh = 0;
|
Chris@76
|
98 do
|
Chris@76
|
99 {
|
Chris@76
|
100 $this->FirstCode = $this->GetCode($data, $bInit);
|
Chris@76
|
101 $this->OldCode = $this->FirstCode;
|
Chris@76
|
102 }
|
Chris@76
|
103 while ($this->FirstCode == $this->ClearCode);
|
Chris@76
|
104
|
Chris@76
|
105 return $this->FirstCode;
|
Chris@76
|
106 }
|
Chris@76
|
107
|
Chris@76
|
108 if ($this->sp > 0)
|
Chris@76
|
109 {
|
Chris@76
|
110 $this->sp--;
|
Chris@76
|
111 return $this->Stack[$this->sp];
|
Chris@76
|
112 }
|
Chris@76
|
113
|
Chris@76
|
114 while (($Code = $this->GetCode($data, $bInit)) >= 0)
|
Chris@76
|
115 {
|
Chris@76
|
116 if ($Code == $this->ClearCode)
|
Chris@76
|
117 {
|
Chris@76
|
118 for ($i = 0; $i < $this->ClearCode; $i++)
|
Chris@76
|
119 {
|
Chris@76
|
120 $this->Next[$i] = 0;
|
Chris@76
|
121 $this->Vals[$i] = $i;
|
Chris@76
|
122 }
|
Chris@76
|
123
|
Chris@76
|
124 for (; $i < (1 << $this->MAX_LZW_BITS); $i++)
|
Chris@76
|
125 {
|
Chris@76
|
126 $this->Next[$i] = 0;
|
Chris@76
|
127 $this->Vals[$i] = 0;
|
Chris@76
|
128 }
|
Chris@76
|
129
|
Chris@76
|
130 $this->CodeSize = $this->SetCodeSize + 1;
|
Chris@76
|
131 $this->MaxCodeSize = $this->ClearCode << 1;
|
Chris@76
|
132 $this->MaxCode = $this->ClearCode + 2;
|
Chris@76
|
133 $this->sp = 0;
|
Chris@76
|
134 $this->FirstCode = $this->GetCode($data, $bInit);
|
Chris@76
|
135 $this->OldCode = $this->FirstCode;
|
Chris@76
|
136
|
Chris@76
|
137 return $this->FirstCode;
|
Chris@76
|
138 }
|
Chris@76
|
139
|
Chris@76
|
140 if ($Code == $this->EndCode)
|
Chris@76
|
141 return -2;
|
Chris@76
|
142
|
Chris@76
|
143 $InCode = $Code;
|
Chris@76
|
144 if ($Code >= $this->MaxCode)
|
Chris@76
|
145 {
|
Chris@76
|
146 $this->Stack[$this->sp] = $this->FirstCode;
|
Chris@76
|
147 $this->sp++;
|
Chris@76
|
148 $Code = $this->OldCode;
|
Chris@76
|
149 }
|
Chris@76
|
150
|
Chris@76
|
151 while ($Code >= $this->ClearCode)
|
Chris@76
|
152 {
|
Chris@76
|
153 $this->Stack[$this->sp] = $this->Vals[$Code];
|
Chris@76
|
154 $this->sp++;
|
Chris@76
|
155
|
Chris@76
|
156 if ($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
|
Chris@76
|
157 return -1;
|
Chris@76
|
158
|
Chris@76
|
159 $Code = $this->Next[$Code];
|
Chris@76
|
160 }
|
Chris@76
|
161
|
Chris@76
|
162 $this->FirstCode = $this->Vals[$Code];
|
Chris@76
|
163 $this->Stack[$this->sp] = $this->FirstCode;
|
Chris@76
|
164 $this->sp++;
|
Chris@76
|
165
|
Chris@76
|
166 if (($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS))
|
Chris@76
|
167 {
|
Chris@76
|
168 $this->Next[$Code] = $this->OldCode;
|
Chris@76
|
169 $this->Vals[$Code] = $this->FirstCode;
|
Chris@76
|
170 $this->MaxCode++;
|
Chris@76
|
171
|
Chris@76
|
172 if (($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS)))
|
Chris@76
|
173 {
|
Chris@76
|
174 $this->MaxCodeSize *= 2;
|
Chris@76
|
175 $this->CodeSize++;
|
Chris@76
|
176 }
|
Chris@76
|
177 }
|
Chris@76
|
178
|
Chris@76
|
179 $this->OldCode = $InCode;
|
Chris@76
|
180 if ($this->sp > 0)
|
Chris@76
|
181 {
|
Chris@76
|
182 $this->sp--;
|
Chris@76
|
183 return $this->Stack[$this->sp];
|
Chris@76
|
184 }
|
Chris@76
|
185 }
|
Chris@76
|
186
|
Chris@76
|
187 return $Code;
|
Chris@76
|
188 }
|
Chris@76
|
189
|
Chris@76
|
190 public function GetCode(&$data, $bInit)
|
Chris@76
|
191 {
|
Chris@76
|
192 if ($bInit)
|
Chris@76
|
193 {
|
Chris@76
|
194 $this->CurBit = 0;
|
Chris@76
|
195 $this->LastBit = 0;
|
Chris@76
|
196 $this->Done = 0;
|
Chris@76
|
197 $this->LastByte = 2;
|
Chris@76
|
198
|
Chris@76
|
199 return 1;
|
Chris@76
|
200 }
|
Chris@76
|
201
|
Chris@76
|
202 if (($this->CurBit + $this->CodeSize) >= $this->LastBit)
|
Chris@76
|
203 {
|
Chris@76
|
204 if ($this->Done)
|
Chris@76
|
205 {
|
Chris@76
|
206 // Ran off the end of my bits...
|
Chris@76
|
207 if ($this->CurBit >= $this->LastBit)
|
Chris@76
|
208 return 0;
|
Chris@76
|
209
|
Chris@76
|
210 return -1;
|
Chris@76
|
211 }
|
Chris@76
|
212
|
Chris@76
|
213 $this->Buf[0] = $this->Buf[$this->LastByte - 2];
|
Chris@76
|
214 $this->Buf[1] = $this->Buf[$this->LastByte - 1];
|
Chris@76
|
215
|
Chris@76
|
216 $count = ord($data[0]);
|
Chris@76
|
217 $data = substr($data, 1);
|
Chris@76
|
218
|
Chris@76
|
219 if ($count)
|
Chris@76
|
220 {
|
Chris@76
|
221 for ($i = 0; $i < $count; $i++)
|
Chris@76
|
222 $this->Buf[2 + $i] = ord($data{$i});
|
Chris@76
|
223
|
Chris@76
|
224 $data = substr($data, $count);
|
Chris@76
|
225 }
|
Chris@76
|
226 else
|
Chris@76
|
227 $this->Done = 1;
|
Chris@76
|
228
|
Chris@76
|
229 $this->LastByte = 2 + $count;
|
Chris@76
|
230 $this->CurBit = ($this->CurBit - $this->LastBit) + 16;
|
Chris@76
|
231 $this->LastBit = (2 + $count) << 3;
|
Chris@76
|
232 }
|
Chris@76
|
233
|
Chris@76
|
234 $iRet = 0;
|
Chris@76
|
235 for ($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++)
|
Chris@76
|
236 $iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j;
|
Chris@76
|
237
|
Chris@76
|
238 $this->CurBit += $this->CodeSize;
|
Chris@76
|
239 return $iRet;
|
Chris@76
|
240 }
|
Chris@76
|
241 }
|
Chris@76
|
242
|
Chris@76
|
243 class gif_color_table
|
Chris@76
|
244 {
|
Chris@76
|
245 public $m_nColors;
|
Chris@76
|
246 public $m_arColors;
|
Chris@76
|
247
|
Chris@76
|
248 public function __construct()
|
Chris@76
|
249 {
|
Chris@76
|
250 unset($this->m_nColors, $this->m_arColors);
|
Chris@76
|
251 }
|
Chris@76
|
252
|
Chris@76
|
253 public function load($lpData, $num)
|
Chris@76
|
254 {
|
Chris@76
|
255 $this->m_nColors = 0;
|
Chris@76
|
256 $this->m_arColors = array();
|
Chris@76
|
257
|
Chris@76
|
258 for ($i = 0; $i < $num; $i++)
|
Chris@76
|
259 {
|
Chris@76
|
260 $rgb = substr($lpData, $i * 3, 3);
|
Chris@76
|
261 if (strlen($rgb) < 3)
|
Chris@76
|
262 return false;
|
Chris@76
|
263
|
Chris@76
|
264 $this->m_arColors[] = (ord($rgb[2]) << 16) + (ord($rgb[1]) << 8) + ord($rgb[0]);
|
Chris@76
|
265 $this->m_nColors++;
|
Chris@76
|
266 }
|
Chris@76
|
267
|
Chris@76
|
268 return true;
|
Chris@76
|
269 }
|
Chris@76
|
270
|
Chris@76
|
271 public function toString()
|
Chris@76
|
272 {
|
Chris@76
|
273 $ret = '';
|
Chris@76
|
274
|
Chris@76
|
275 for ($i = 0; $i < $this->m_nColors; $i++)
|
Chris@76
|
276 {
|
Chris@76
|
277 $ret .=
|
Chris@76
|
278 chr(($this->m_arColors[$i] & 0x000000FF)) . // R
|
Chris@76
|
279 chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G
|
Chris@76
|
280 chr(($this->m_arColors[$i] & 0x00FF0000) >> 16); // B
|
Chris@76
|
281 }
|
Chris@76
|
282
|
Chris@76
|
283 return $ret;
|
Chris@76
|
284 }
|
Chris@76
|
285
|
Chris@76
|
286 public function colorIndex($rgb)
|
Chris@76
|
287 {
|
Chris@76
|
288 $rgb = intval($rgb) & 0xFFFFFF;
|
Chris@76
|
289 $r1 = ($rgb & 0x0000FF);
|
Chris@76
|
290 $g1 = ($rgb & 0x00FF00) >> 8;
|
Chris@76
|
291 $b1 = ($rgb & 0xFF0000) >> 16;
|
Chris@76
|
292 $idx = -1;
|
Chris@76
|
293
|
Chris@76
|
294 for ($i = 0; $i < $this->m_nColors; $i++)
|
Chris@76
|
295 {
|
Chris@76
|
296 $r2 = ($this->m_arColors[$i] & 0x000000FF);
|
Chris@76
|
297 $g2 = ($this->m_arColors[$i] & 0x0000FF00) >> 8;
|
Chris@76
|
298 $b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
|
Chris@76
|
299 $d = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
|
Chris@76
|
300
|
Chris@76
|
301 if (($idx == -1) || ($d < $dif))
|
Chris@76
|
302 {
|
Chris@76
|
303 $idx = $i;
|
Chris@76
|
304 $dif = $d;
|
Chris@76
|
305 }
|
Chris@76
|
306 }
|
Chris@76
|
307
|
Chris@76
|
308 return $idx;
|
Chris@76
|
309 }
|
Chris@76
|
310 }
|
Chris@76
|
311
|
Chris@76
|
312 class gif_file_header
|
Chris@76
|
313 {
|
Chris@76
|
314 public $m_lpVer, $m_nWidth, $m_nHeight, $m_bGlobalClr, $m_nColorRes;
|
Chris@76
|
315 public $m_bSorted, $m_nTableSize, $m_nBgColor, $m_nPixelRatio;
|
Chris@76
|
316 public $m_colorTable;
|
Chris@76
|
317
|
Chris@76
|
318 public function __construct()
|
Chris@76
|
319 {
|
Chris@76
|
320 unset($this->m_lpVer, $this->m_nWidth, $this->m_nHeight, $this->m_bGlobalClr, $this->m_nColorRes);
|
Chris@76
|
321 unset($this->m_bSorted, $this->m_nTableSize, $this->m_nBgColor, $this->m_nPixelRatio, $this->m_colorTable);
|
Chris@76
|
322 }
|
Chris@76
|
323
|
Chris@76
|
324 public function load($lpData, &$hdrLen)
|
Chris@76
|
325 {
|
Chris@76
|
326 $hdrLen = 0;
|
Chris@76
|
327
|
Chris@76
|
328 $this->m_lpVer = substr($lpData, 0, 6);
|
Chris@76
|
329 if (($this->m_lpVer != 'GIF87a') && ($this->m_lpVer != 'GIF89a'))
|
Chris@76
|
330 return false;
|
Chris@76
|
331
|
Chris@76
|
332 list ($this->m_nWidth, $this->m_nHeight) = array_values(unpack('v2', substr($lpData, 6, 4)));
|
Chris@76
|
333
|
Chris@76
|
334 if (!$this->m_nWidth || !$this->m_nHeight)
|
Chris@76
|
335 return false;
|
Chris@76
|
336
|
Chris@76
|
337 $b = ord(substr($lpData, 10, 1));
|
Chris@76
|
338 $this->m_bGlobalClr = ($b & 0x80) ? true : false;
|
Chris@76
|
339 $this->m_nColorRes = ($b & 0x70) >> 4;
|
Chris@76
|
340 $this->m_bSorted = ($b & 0x08) ? true : false;
|
Chris@76
|
341 $this->m_nTableSize = 2 << ($b & 0x07);
|
Chris@76
|
342 $this->m_nBgColor = ord(substr($lpData, 11, 1));
|
Chris@76
|
343 $this->m_nPixelRatio = ord(substr($lpData, 12, 1));
|
Chris@76
|
344 $hdrLen = 13;
|
Chris@76
|
345
|
Chris@76
|
346 if ($this->m_bGlobalClr)
|
Chris@76
|
347 {
|
Chris@76
|
348 $this->m_colorTable = new gif_color_table();
|
Chris@76
|
349 if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize))
|
Chris@76
|
350 return false;
|
Chris@76
|
351
|
Chris@76
|
352 $hdrLen += 3 * $this->m_nTableSize;
|
Chris@76
|
353 }
|
Chris@76
|
354
|
Chris@76
|
355 return true;
|
Chris@76
|
356 }
|
Chris@76
|
357 }
|
Chris@76
|
358
|
Chris@76
|
359 class gif_image_header
|
Chris@76
|
360 {
|
Chris@76
|
361 public $m_nLeft, $m_nTop, $m_nWidth, $m_nHeight, $m_bLocalClr;
|
Chris@76
|
362 public $m_bInterlace, $m_bSorted, $m_nTableSize, $m_colorTable;
|
Chris@76
|
363
|
Chris@76
|
364 public function __construct()
|
Chris@76
|
365 {
|
Chris@76
|
366 unset($this->m_nLeft, $this->m_nTop, $this->m_nWidth, $this->m_nHeight, $this->m_bLocalClr);
|
Chris@76
|
367 unset($this->m_bInterlace, $this->m_bSorted, $this->m_nTableSize, $this->m_colorTable);
|
Chris@76
|
368 }
|
Chris@76
|
369
|
Chris@76
|
370 public function load($lpData, &$hdrLen)
|
Chris@76
|
371 {
|
Chris@76
|
372 $hdrLen = 0;
|
Chris@76
|
373
|
Chris@76
|
374 // Get the width/height/etc. from the header.
|
Chris@76
|
375 list ($this->m_nLeft, $this->m_nTop, $this->m_nWidth, $this->m_nHeight) = array_values(unpack('v4', substr($lpData, 0, 8)));
|
Chris@76
|
376
|
Chris@76
|
377 if (!$this->m_nWidth || !$this->m_nHeight)
|
Chris@76
|
378 return false;
|
Chris@76
|
379
|
Chris@76
|
380 $b = ord($lpData[8]);
|
Chris@76
|
381 $this->m_bLocalClr = ($b & 0x80) ? true : false;
|
Chris@76
|
382 $this->m_bInterlace = ($b & 0x40) ? true : false;
|
Chris@76
|
383 $this->m_bSorted = ($b & 0x20) ? true : false;
|
Chris@76
|
384 $this->m_nTableSize = 2 << ($b & 0x07);
|
Chris@76
|
385 $hdrLen = 9;
|
Chris@76
|
386
|
Chris@76
|
387 if ($this->m_bLocalClr)
|
Chris@76
|
388 {
|
Chris@76
|
389 $this->m_colorTable = new gif_color_table();
|
Chris@76
|
390 if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize))
|
Chris@76
|
391 return false;
|
Chris@76
|
392
|
Chris@76
|
393 $hdrLen += 3 * $this->m_nTableSize;
|
Chris@76
|
394 }
|
Chris@76
|
395
|
Chris@76
|
396 return true;
|
Chris@76
|
397 }
|
Chris@76
|
398 }
|
Chris@76
|
399
|
Chris@76
|
400 class gif_image
|
Chris@76
|
401 {
|
Chris@76
|
402 public $m_disp, $m_bUser, $m_bTrans, $m_nDelay, $m_nTrans, $m_lpComm;
|
Chris@76
|
403 public $m_gih, $m_data, $m_lzw;
|
Chris@76
|
404
|
Chris@76
|
405 public function __construct()
|
Chris@76
|
406 {
|
Chris@76
|
407 unset($this->m_disp, $this->m_bUser, $this->m_nDelay, $this->m_nTrans, $this->m_lpComm, $this->m_data);
|
Chris@76
|
408 $this->m_gih = new gif_image_header();
|
Chris@76
|
409 $this->m_lzw = new gif_lzw_compression();
|
Chris@76
|
410 }
|
Chris@76
|
411
|
Chris@76
|
412 public function load($data, &$datLen)
|
Chris@76
|
413 {
|
Chris@76
|
414 $datLen = 0;
|
Chris@76
|
415
|
Chris@76
|
416 while (true)
|
Chris@76
|
417 {
|
Chris@76
|
418 $b = ord($data[0]);
|
Chris@76
|
419 $data = substr($data, 1);
|
Chris@76
|
420 $datLen++;
|
Chris@76
|
421
|
Chris@76
|
422 switch ($b)
|
Chris@76
|
423 {
|
Chris@76
|
424 // Extension...
|
Chris@76
|
425 case 0x21:
|
Chris@76
|
426 $len = 0;
|
Chris@76
|
427 if (!$this->skipExt($data, $len))
|
Chris@76
|
428 return false;
|
Chris@76
|
429
|
Chris@76
|
430 $datLen += $len;
|
Chris@76
|
431 break;
|
Chris@76
|
432
|
Chris@76
|
433 // Image...
|
Chris@76
|
434 case 0x2C:
|
Chris@76
|
435 // Load the header and color table.
|
Chris@76
|
436 $len = 0;
|
Chris@76
|
437 if (!$this->m_gih->load($data, $len))
|
Chris@76
|
438 return false;
|
Chris@76
|
439
|
Chris@76
|
440 $data = substr($data, $len);
|
Chris@76
|
441 $datLen += $len;
|
Chris@76
|
442
|
Chris@76
|
443 // Decompress the data, and ride on home ;).
|
Chris@76
|
444 $len = 0;
|
Chris@76
|
445 if (!($this->m_data = $this->m_lzw->decompress($data, $len)))
|
Chris@76
|
446 return false;
|
Chris@76
|
447
|
Chris@76
|
448 $data = substr($data, $len);
|
Chris@76
|
449 $datLen += $len;
|
Chris@76
|
450
|
Chris@76
|
451 if ($this->m_gih->m_bInterlace)
|
Chris@76
|
452 $this->deInterlace();
|
Chris@76
|
453
|
Chris@76
|
454 return true;
|
Chris@76
|
455
|
Chris@76
|
456 case 0x3B: // EOF
|
Chris@76
|
457 default:
|
Chris@76
|
458 return false;
|
Chris@76
|
459 }
|
Chris@76
|
460 }
|
Chris@76
|
461 return false;
|
Chris@76
|
462 }
|
Chris@76
|
463
|
Chris@76
|
464 public function skipExt(&$data, &$extLen)
|
Chris@76
|
465 {
|
Chris@76
|
466 $extLen = 0;
|
Chris@76
|
467
|
Chris@76
|
468 $b = ord($data[0]);
|
Chris@76
|
469 $data = substr($data, 1);
|
Chris@76
|
470 $extLen++;
|
Chris@76
|
471
|
Chris@76
|
472 switch ($b)
|
Chris@76
|
473 {
|
Chris@76
|
474 // Graphic Control...
|
Chris@76
|
475 case 0xF9:
|
Chris@76
|
476 $b = ord($data[1]);
|
Chris@76
|
477 $this->m_disp = ($b & 0x1C) >> 2;
|
Chris@76
|
478 $this->m_bUser = ($b & 0x02) ? true : false;
|
Chris@76
|
479 $this->m_bTrans = ($b & 0x01) ? true : false;
|
Chris@76
|
480 list ($this->m_nDelay) = array_values(unpack('v', substr($data, 2, 2)));
|
Chris@76
|
481 $this->m_nTrans = ord($data[4]);
|
Chris@76
|
482 break;
|
Chris@76
|
483
|
Chris@76
|
484 // Comment...
|
Chris@76
|
485 case 0xFE:
|
Chris@76
|
486 $this->m_lpComm = substr($data, 1, ord($data[0]));
|
Chris@76
|
487 break;
|
Chris@76
|
488
|
Chris@76
|
489 // Plain text...
|
Chris@76
|
490 case 0x01:
|
Chris@76
|
491 break;
|
Chris@76
|
492
|
Chris@76
|
493 // Application...
|
Chris@76
|
494 case 0xFF:
|
Chris@76
|
495 break;
|
Chris@76
|
496 }
|
Chris@76
|
497
|
Chris@76
|
498 // Skip default as defs may change.
|
Chris@76
|
499 $b = ord($data[0]);
|
Chris@76
|
500 $data = substr($data, 1);
|
Chris@76
|
501 $extLen++;
|
Chris@76
|
502 while ($b > 0)
|
Chris@76
|
503 {
|
Chris@76
|
504 $data = substr($data, $b);
|
Chris@76
|
505 $extLen += $b;
|
Chris@76
|
506 $b = ord($data[0]);
|
Chris@76
|
507 $data = substr($data, 1);
|
Chris@76
|
508 $extLen++;
|
Chris@76
|
509 }
|
Chris@76
|
510 return true;
|
Chris@76
|
511 }
|
Chris@76
|
512
|
Chris@76
|
513 public function deInterlace()
|
Chris@76
|
514 {
|
Chris@76
|
515 $data = $this->m_data;
|
Chris@76
|
516
|
Chris@76
|
517 for ($i = 0; $i < 4; $i++)
|
Chris@76
|
518 {
|
Chris@76
|
519 switch ($i)
|
Chris@76
|
520 {
|
Chris@76
|
521 case 0:
|
Chris@76
|
522 $s = 8;
|
Chris@76
|
523 $y = 0;
|
Chris@76
|
524 break;
|
Chris@76
|
525
|
Chris@76
|
526 case 1:
|
Chris@76
|
527 $s = 8;
|
Chris@76
|
528 $y = 4;
|
Chris@76
|
529 break;
|
Chris@76
|
530
|
Chris@76
|
531 case 2:
|
Chris@76
|
532 $s = 4;
|
Chris@76
|
533 $y = 2;
|
Chris@76
|
534 break;
|
Chris@76
|
535
|
Chris@76
|
536 case 3:
|
Chris@76
|
537 $s = 2;
|
Chris@76
|
538 $y = 1;
|
Chris@76
|
539 break;
|
Chris@76
|
540 }
|
Chris@76
|
541
|
Chris@76
|
542 for (; $y < $this->m_gih->m_nHeight; $y += $s)
|
Chris@76
|
543 {
|
Chris@76
|
544 $lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
|
Chris@76
|
545 $this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
|
Chris@76
|
546
|
Chris@76
|
547 $data =
|
Chris@76
|
548 substr($data, 0, $y * $this->m_gih->m_nWidth) .
|
Chris@76
|
549 $lne .
|
Chris@76
|
550 substr($data, ($y + 1) * $this->m_gih->m_nWidth);
|
Chris@76
|
551 }
|
Chris@76
|
552 }
|
Chris@76
|
553
|
Chris@76
|
554 $this->m_data = $data;
|
Chris@76
|
555 }
|
Chris@76
|
556 }
|
Chris@76
|
557
|
Chris@76
|
558 class gif_file
|
Chris@76
|
559 {
|
Chris@76
|
560 public $header, $image, $data, $loaded;
|
Chris@76
|
561
|
Chris@76
|
562 public function __construct()
|
Chris@76
|
563 {
|
Chris@76
|
564 $this->data = '';
|
Chris@76
|
565 $this->loaded = false;
|
Chris@76
|
566 $this->header = new gif_file_header();
|
Chris@76
|
567 $this->image = new gif_image();
|
Chris@76
|
568 }
|
Chris@76
|
569
|
Chris@76
|
570 public function loadFile($filename, $iIndex)
|
Chris@76
|
571 {
|
Chris@76
|
572 if ($iIndex < 0)
|
Chris@76
|
573 return false;
|
Chris@76
|
574
|
Chris@76
|
575 $this->data = @file_get_contents($filename);
|
Chris@76
|
576 if ($this->data === false)
|
Chris@76
|
577 return false;
|
Chris@76
|
578
|
Chris@76
|
579 // Tell the header to load up....
|
Chris@76
|
580 $len = 0;
|
Chris@76
|
581 if (!$this->header->load($this->data, $len))
|
Chris@76
|
582 return false;
|
Chris@76
|
583
|
Chris@76
|
584 $this->data = substr($this->data, $len);
|
Chris@76
|
585
|
Chris@76
|
586 // Keep reading (at least once) so we get to the actual image we're looking for.
|
Chris@76
|
587 for ($j = 0; $j <= $iIndex; $j++)
|
Chris@76
|
588 {
|
Chris@76
|
589 $imgLen = 0;
|
Chris@76
|
590 if (!$this->image->load($this->data, $imgLen))
|
Chris@76
|
591 return false;
|
Chris@76
|
592
|
Chris@76
|
593 $this->data = substr($this->data, $imgLen);
|
Chris@76
|
594 }
|
Chris@76
|
595
|
Chris@76
|
596 $this->loaded = true;
|
Chris@76
|
597 return true;
|
Chris@76
|
598 }
|
Chris@76
|
599
|
Chris@76
|
600 public function get_png_data($background_color)
|
Chris@76
|
601 {
|
Chris@76
|
602 if (!$this->loaded)
|
Chris@76
|
603 return false;
|
Chris@76
|
604
|
Chris@76
|
605 // Prepare the color table.
|
Chris@76
|
606 if ($this->image->m_gih->m_bLocalClr)
|
Chris@76
|
607 {
|
Chris@76
|
608 $colors = $this->image->m_gih->m_nTableSize;
|
Chris@76
|
609 $pal = $this->image->m_gih->m_colorTable->toString();
|
Chris@76
|
610
|
Chris@76
|
611 if ($background_color != -1)
|
Chris@76
|
612 $background_color = $this->image->m_gih->m_colorTable->colorIndex($background_color);
|
Chris@76
|
613 }
|
Chris@76
|
614 elseif ($this->header->m_bGlobalClr)
|
Chris@76
|
615 {
|
Chris@76
|
616 $colors = $this->header->m_nTableSize;
|
Chris@76
|
617 $pal = $this->header->m_colorTable->toString();
|
Chris@76
|
618
|
Chris@76
|
619 if ($background_color != -1)
|
Chris@76
|
620 $background_color = $this->header->m_colorTable->colorIndex($background_color);
|
Chris@76
|
621 }
|
Chris@76
|
622 else
|
Chris@76
|
623 {
|
Chris@76
|
624 $colors = 0;
|
Chris@76
|
625 $background_color = -1;
|
Chris@76
|
626 }
|
Chris@76
|
627
|
Chris@76
|
628 if ($background_color == -1)
|
Chris@76
|
629 $background_color = $this->header->m_nBgColor;
|
Chris@76
|
630
|
Chris@76
|
631 $data = &$this->image->m_data;
|
Chris@76
|
632 $header = &$this->image->m_gih;
|
Chris@76
|
633
|
Chris@76
|
634 $i = 0;
|
Chris@76
|
635 $bmp = '';
|
Chris@76
|
636
|
Chris@76
|
637 // Prepare the bitmap itself.
|
Chris@76
|
638 for ($y = 0; $y < $this->header->m_nHeight; $y++)
|
Chris@76
|
639 {
|
Chris@76
|
640 $bmp .= "\x00";
|
Chris@76
|
641
|
Chris@76
|
642 for ($x = 0; $x < $this->header->m_nWidth; $x++, $i++)
|
Chris@76
|
643 {
|
Chris@76
|
644 // Is this in the proper range? If so, get the specific pixel data...
|
Chris@76
|
645 if ($x >= $header->m_nLeft && $y >= $header->m_nTop && $x < ($header->m_nLeft + $header->m_nWidth) && $y < ($header->m_nTop + $header->m_nHeight))
|
Chris@76
|
646 $bmp .= $data{$i};
|
Chris@76
|
647 // Otherwise, this is background...
|
Chris@76
|
648 else
|
Chris@76
|
649 $bmp .= chr($background_color);
|
Chris@76
|
650 }
|
Chris@76
|
651 }
|
Chris@76
|
652
|
Chris@76
|
653 $bmp = gzcompress($bmp, 9);
|
Chris@76
|
654
|
Chris@76
|
655 // Output the basic signature first of all.
|
Chris@76
|
656 $out = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
|
Chris@76
|
657
|
Chris@76
|
658 // Now, we want the header...
|
Chris@76
|
659 $out .= "\x00\x00\x00\x0D";
|
Chris@76
|
660 $tmp = 'IHDR' . pack('N', (int) $this->header->m_nWidth) . pack('N', (int) $this->header->m_nHeight) . "\x08\x03\x00\x00\x00";
|
Chris@76
|
661 $out .= $tmp . pack('N', smf_crc32($tmp));
|
Chris@76
|
662
|
Chris@76
|
663 // The palette, assuming we have one to speak of...
|
Chris@76
|
664 if ($colors > 0)
|
Chris@76
|
665 {
|
Chris@76
|
666 $out .= pack('N', (int) $colors * 3);
|
Chris@76
|
667 $tmp = 'PLTE' . $pal;
|
Chris@76
|
668 $out .= $tmp . pack('N', smf_crc32($tmp));
|
Chris@76
|
669 }
|
Chris@76
|
670
|
Chris@76
|
671 // Do we have any transparency we want to make available?
|
Chris@76
|
672 if ($this->image->m_bTrans && $colors > 0)
|
Chris@76
|
673 {
|
Chris@76
|
674 $out .= pack('N', (int) $colors);
|
Chris@76
|
675 $tmp = 'tRNS';
|
Chris@76
|
676
|
Chris@76
|
677 // Stick each color on - full transparency or none.
|
Chris@76
|
678 for ($i = 0; $i < $colors; $i++)
|
Chris@76
|
679 $tmp .= $i == $this->image->m_nTrans ? "\x00" : "\xFF";
|
Chris@76
|
680
|
Chris@76
|
681 $out .= $tmp . pack('N', smf_crc32($tmp));
|
Chris@76
|
682 }
|
Chris@76
|
683
|
Chris@76
|
684 // Here's the data itself!
|
Chris@76
|
685 $out .= pack('N', strlen($bmp));
|
Chris@76
|
686 $tmp = 'IDAT' . $bmp;
|
Chris@76
|
687 $out .= $tmp . pack('N', smf_crc32($tmp));
|
Chris@76
|
688
|
Chris@76
|
689 // EOF marker...
|
Chris@76
|
690 $out .= "\x00\x00\x00\x00" . 'IEND' . "\xAE\x42\x60\x82";
|
Chris@76
|
691
|
Chris@76
|
692 return $out;
|
Chris@76
|
693 }
|
Chris@76
|
694 }
|
Chris@76
|
695
|
Chris@76
|
696 // crc32 doesn't work as expected on 64-bit functions - make our own.
|
Chris@76
|
697 // http://www.php.net/crc32#79567
|
Chris@76
|
698 if (!function_exists('smf_crc32'))
|
Chris@76
|
699 {
|
Chris@76
|
700 function smf_crc32($number)
|
Chris@76
|
701 {
|
Chris@76
|
702 $crc = crc32($number);
|
Chris@76
|
703
|
Chris@76
|
704 if ($crc & 0x80000000)
|
Chris@76
|
705 {
|
Chris@76
|
706 $crc ^= 0xffffffff;
|
Chris@76
|
707 $crc += 1;
|
Chris@76
|
708 $crc = -$crc;
|
Chris@76
|
709 }
|
Chris@76
|
710
|
Chris@76
|
711 return $crc;
|
Chris@76
|
712 }
|
Chris@76
|
713 }
|
Chris@76
|
714
|
Chris@76
|
715 ?> |