PageRenderTime 53ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/GifMagic/Decoder.php

https://bitbucket.org/webvariants/gifmagic
PHP | 287 lines | 190 code | 57 blank | 40 comment | 33 complexity | 756c705f827844bb7f64952f74653b04 MD5 | raw file
  1. <?php
  2. /*
  3. * Copyright (c) 2011, webvariants GbR, http://www.webvariants.de
  4. *
  5. * This file is released under the terms of the MIT license. You can find the
  6. * complete text in the attached LICENSE file or online at:
  7. *
  8. * http://www.opensource.org/licenses/mit-license.php
  9. *
  10. * based on GIFDecoder by Lรกszlรณ Zsidi
  11. * http://www.phpclasses.org/package/3163-PHP-Generate-GIF-animations-from-a-set-of-GIF-images.html
  12. * http://www.phpclasses.org/package/3234-PHP-Split-GIF-animations-into-multiple-images.html
  13. * http://www.gifs.hu
  14. */
  15. /**
  16. * @author robert.koppsieker@webvariants.de
  17. */
  18. class GifMagic_Decoder {
  19. private $transparentR = -1;
  20. private $transparentG = -1;
  21. private $transparentB = -1;
  22. private $transparentI = 0;
  23. private $buffer = array();
  24. private $arrays = array();
  25. private $delays = array();
  26. private $dispos = array();
  27. private $offset = array();
  28. private $stream = '';
  29. private $string = '';
  30. private $bfseek = 0;
  31. private $anloop = 0;
  32. private $streamLength = 0;
  33. private $screen = array();
  34. private $screenWidth = null;
  35. private $screenHeight = null;
  36. private $global = array();
  37. private $sorted;
  38. private $colorS;
  39. private $colorC;
  40. private $colorF;
  41. public function __construct($gifFileContent) {
  42. $this->stream = $gifFileContent;
  43. $this->streamLength = strlen($this->stream);
  44. $this->getByte(6); // GIF89a
  45. $this->getByte(7); // Logical Screen Descriptor
  46. $packed = $this->buffer[4]; // Packed Fields
  47. $this->screen = $this->buffer;
  48. $this->screenWidth = $this->buffer[0]; // Logical Screen Width
  49. $this->screenHeight = $this->buffer[2]; // Logical Screen Height
  50. $this->colorF = $packed & 0x80 ? 1 : 0; // Global Color Table Flag
  51. $this->colorC = $packed & 0x07; // Color Resulution
  52. $this->sorted = $packed & 0x08 ? 1 : 0; // Sort Flag
  53. $this->colorS = 2 << $this->colorC; // Size of Global Color Table
  54. // read global color table
  55. if ($this->colorF === 1) {
  56. $this->getByte(3*$this->colorS);
  57. $this->global = $this->buffer;
  58. }
  59. for (;;) {
  60. if (!$this->getByte(1)) break;
  61. switch ($this->buffer[0]) {
  62. case 0x21:
  63. $this->readExtensions();
  64. break;
  65. case 0x2C:
  66. $this->readDescriptor();
  67. break;
  68. case 0x3B:
  69. break 2;
  70. }
  71. }
  72. }
  73. private function readExtensions() {
  74. $this->getByte(1);
  75. // application extension
  76. if ($this->buffer[0] === 0xFF) {
  77. for (;;) {
  78. $this->getByte(1);
  79. // if byte size is zero, return
  80. if (($u = $this->buffer[0]) === 0) return false;
  81. // get bytes in length of byte size
  82. $this->getByte($u);
  83. // if byte size is 3
  84. if ($u == 3) {
  85. // get loop counter (0 to 65535), zero means infinite
  86. $this->anloop = ($this->buffer[1] | $this->buffer[2] << 8);
  87. }
  88. }
  89. }
  90. // other applications
  91. else {
  92. for (;;) {
  93. $this->getByte(1);
  94. // if byte size is zero, return
  95. if (($u = $this->buffer[0]) === 0) return false;
  96. // get bytes in length of byte size
  97. $this->getByte($u);
  98. // if graphics control extension
  99. if ($u === 4) {
  100. // disposal method
  101. if (isset($this->buffer[4]) && $this->buffer[4] & 0x80) {
  102. $this->dispos[] = ($this->buffer[0] >> 2) - 1;
  103. }
  104. else {
  105. $this->dispos[] = ($this->buffer[0] >> 2) - 0;
  106. }
  107. // delay
  108. $this->delays[] = ($this->buffer[1] | $this->buffer[2] << 8);
  109. // transparent color index
  110. if ($this->buffer[3]) {
  111. $this->transparentI = $this->buffer[3];
  112. }
  113. }
  114. }
  115. }
  116. }
  117. private function readDescriptor() {
  118. $GIF_screen = array();
  119. $this->getByte(9);
  120. $GIF_screen = $this->buffer;
  121. $imageLeft = $this->getInt(array($this->buffer[0], $this->buffer[1]));
  122. $imageTop = $this->getInt(array($this->buffer[2], $this->buffer[3]));
  123. $this->offset[] = array($imageLeft, $imageTop);
  124. $GIF_colorF = $this->buffer [8] & 0x80 ? 1 : 0;
  125. if ($GIF_colorF) {
  126. $GIF_code = $this->buffer [8] & 0x07;
  127. $GIF_sort = $this->buffer [8] & 0x20 ? 1 : 0;
  128. }
  129. else {
  130. $GIF_code = $this->colorC;
  131. $GIF_sort = $this->sorted;
  132. }
  133. $GIF_size = 2 << $GIF_code;
  134. $this->screen[4] &= 0x70;
  135. $this->screen[4] |= 0x80;
  136. $this->screen[4] |= $GIF_code;
  137. if ($GIF_sort) {
  138. $this->screen[4] |= 0x08;
  139. }
  140. /*
  141. *
  142. * GIF Data Begin
  143. *
  144. */
  145. if ($this->transparentI) {
  146. $this->string = 'GIF89a';
  147. }
  148. else {
  149. $this->string = 'GIF87a';
  150. }
  151. $this->putByte($this->screen);
  152. if ($GIF_colorF == 1) {
  153. $this->getByte(3 * $GIF_size);
  154. if ($this->transparentI) {
  155. $this->transparentR = $this->buffer[3 * $this->transparentI + 0];
  156. $this->transparentG = $this->buffer[3 * $this->transparentI + 1];
  157. $this->transparentB = $this->buffer[3 * $this->transparentI + 2];
  158. }
  159. $this->putByte($this->buffer);
  160. }
  161. else {
  162. if ($this->transparentI) {
  163. $this->transparentR = $this->global[3 * $this->transparentI + 0];
  164. $this->transparentG = $this->global[3 * $this->transparentI + 1];
  165. $this->transparentB = $this->global[3 * $this->transparentI + 2];
  166. }
  167. $this->putByte($this->global);
  168. }
  169. if ($this->transparentI) {
  170. $this->string .= "!\xF9\x04\x01\x00\x00".chr($this->transparentI)."\x00";
  171. }
  172. // \x1 == 000 000 0 1
  173. $this->string .= chr(0x2C);
  174. $GIF_screen[8] &= 0x40;
  175. $this->putByte($GIF_screen);
  176. $this->getByte(1);
  177. $this->putByte($this->buffer);
  178. for (;;) {
  179. $this->getByte(1);
  180. $this->putByte($this->buffer);
  181. if (($u = $this->buffer[0]) === 0) {
  182. break;
  183. }
  184. $this->getByte($u);
  185. $this->putByte($this->buffer);
  186. }
  187. $this->string .= chr(0x3B);
  188. /*
  189. *
  190. * GIF Data End
  191. *
  192. */
  193. $this->arrays[] = $this->string;
  194. }
  195. private function getByte($len) {
  196. $this->buffer = array();
  197. for ($i = 0; $i < $len; ++$i) {
  198. if ($this->bfseek > $this->streamLength) {
  199. return 0;
  200. }
  201. $this->buffer[] = ord($this->stream[$this->bfseek++]);
  202. }
  203. return 1;
  204. }
  205. private function putByte($bytes) {
  206. foreach ($bytes as $byte) {
  207. $this->string .= chr($byte);
  208. }
  209. }
  210. private function getInt(array $bytes) {
  211. if (count($bytes) === 1) return (int) reset($bytes);
  212. $skew = count($bytes)-1;
  213. $bytes = array_reverse($bytes);
  214. foreach ($bytes as $idx => $byte) {
  215. $bytes[$idx] = ($byte << ($skew*8));
  216. $skew--;
  217. }
  218. return array_sum($bytes);
  219. }
  220. public function getFrames() { return $this->arrays; }
  221. public function getScreenWidth() { return $this->screenWidth; }
  222. public function getScreenHeight() { return $this->screenHeight; }
  223. public function getDelays() { return $this->delays; }
  224. public function getLoop() { return $this->anloop; }
  225. public function getDisposal() { return $this->dispos; }
  226. public function getOffset() { return $this->offset; }
  227. public function getTransparentR() { return $this->transparentR; }
  228. public function getTransparentG() { return $this->transparentG; }
  229. public function getTransparentB() { return $this->transparentB; }
  230. }