pngCompote.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. <?php
  2. namespace PngFile;
  3. define('MAGIC_HEADER', b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A");
  4. define('CGBI_CHUNK_TYPE', 'CgBI');
  5. define('IHDR_CHUNK_TYPE', 'IHDR');
  6. define('IDAT_CHUNK_TYPE', 'IDAT');
  7. define('IEND_CHUNK_TYPE', 'IEND');
  8. define('CHUNK_SIZE_LENGHT', 4);
  9. define('CHUNK_TYPE_LENGHT', 4);
  10. define('CHUNK_CRC_LENGHT', 4);
  11. class PngFile
  12. {
  13. public $filename;
  14. public $handle;
  15. public $width = 0;
  16. public $height = 0;
  17. public $chunks = array();
  18. public function __construct($filename)
  19. {
  20. $this->filename = $filename;
  21. $this->handle = fopen($this->filename, "rb");
  22. $magic = fread($this->handle, 8);
  23. if (!strcmp($magic, MAGIC_HEADER) == 0) {
  24. return FALSE;
  25. }
  26. try {
  27. $idx = ftell($this->handle);
  28. do {
  29. $chunk = new PngChunk($this, $idx);
  30. $this->chunks[] = $chunk;
  31. $idx = $chunk->getNextChunkIdx();
  32. } while (strcmp($chunk->type, IEND_CHUNK_TYPE) != 0);
  33. } catch (UnexpectedValueException $e) {
  34. return FALSE;
  35. }
  36. fclose($this->handle);
  37. $this->handle = NULL;
  38. }
  39. function revertIphone($newFilename)
  40. {
  41. if (!isset($this->isIphone)) {
  42. return FALSE;
  43. }
  44. $this->handle = fopen($this->filename, "rb");
  45. $newHandle = fopen($newFilename, "wb");
  46. fwrite($newHandle, MAGIC_HEADER);
  47. foreach ($this->chunks as $chunk) {
  48. if (strcmp($chunk->type, CGBI_CHUNK_TYPE) != 0) {
  49. if ($chunk->dataLength > 0) {
  50. $res = fseek($this->handle, $chunk->idxStart + CHUNK_TYPE_LENGHT + CHUNK_SIZE_LENGHT, SEEK_SET);
  51. if ($res == -1) {
  52. throw new UnexpectedValueException();
  53. }
  54. if (strcmp($chunk->type, IDAT_CHUNK_TYPE) == 0) {
  55. $data = fread($this->handle, $chunk->dataLength);
  56. $data = @gzinflate($data);
  57. $dataRes = '';
  58. $scanlinesize = 1 + ($this->width * 4);
  59. for ($y = 0; $y < $this->height; $y++) {
  60. if (isset($data[$y * $scanlinesize])) {
  61. $filterType = $data[$y * $scanlinesize];
  62. } else {
  63. return FALSE;
  64. }
  65. $dataRes .= $filterType;
  66. for ($x = 0; $x < $this->width; $x++) {
  67. $pixel = substr($data, ($y * $scanlinesize + 1) + ($x * 4), 4);
  68. $dataRes .= $pixel[2] . $pixel[1] . $pixel[0] . $pixel[3];
  69. }
  70. }
  71. $data = gzcompress($dataRes, 9);
  72. } else {
  73. $data = fread($this->handle, $chunk->dataLength);
  74. }
  75. } else {
  76. $data = '';
  77. }
  78. $dataLen = pack('N', mb_strlen($data, '8bit'));
  79. fwrite($newHandle, $dataLen);
  80. fwrite($newHandle, $chunk->type);
  81. fwrite($newHandle, $data);
  82. $crc = pack('N', crc32($chunk->type . $data));
  83. fwrite($newHandle, $crc);
  84. }
  85. }
  86. fclose($this->handle);
  87. fclose($newHandle);
  88. $this->handle = NULL;
  89. return TRUE;
  90. }
  91. }
  92. class PngChunk
  93. {
  94. public $png;
  95. public $idxStart;
  96. public $type;
  97. public $dataLength;
  98. public $crc;
  99. public function __construct($png, $idx)
  100. {
  101. $this->png = $png;
  102. $this->idxStart = $idx;
  103. $res = fseek($this->png->handle, $idx, SEEK_SET);
  104. if ($res == -1) {
  105. throw new UnexpectedValueException();
  106. }
  107. $val = fread($this->png->handle, CHUNK_SIZE_LENGHT);
  108. if ($val == FALSE) {
  109. throw new UnexpectedValueException();
  110. }
  111. $val = unpack('N', $val);
  112. $this->dataLength = $val[1];
  113. $val = fread($this->png->handle, CHUNK_TYPE_LENGHT);
  114. if ($val == FALSE) {
  115. throw new UnexpectedValueException();
  116. }
  117. $this->type = $val;
  118. $res = fseek($this->png->handle, $this->dataLength, SEEK_CUR);
  119. if ($res == -1) {
  120. throw new UnexpectedValueException();
  121. }
  122. $val = fread($this->png->handle, CHUNK_CRC_LENGHT);
  123. if ($val == FALSE) {
  124. throw new UnexpectedValueException();
  125. }
  126. $this->crc = $val;
  127. if (strcmp($this->type, CGBI_CHUNK_TYPE) == 0) {
  128. $this->png->isIphone = TRUE;
  129. }
  130. if (strcmp($this->type, IHDR_CHUNK_TYPE) == 0) {
  131. $res = fseek($this->png->handle, $this->idxStart + CHUNK_SIZE_LENGHT + CHUNK_TYPE_LENGHT, SEEK_SET);
  132. if ($res == -1) {
  133. throw new UnexpectedValueException();
  134. }
  135. $val = fread($this->png->handle, $this->dataLength);
  136. if ($val == FALSE) {
  137. throw new UnexpectedValueException();
  138. }
  139. $val = unpack('Nwidth/Nheight/Cdepth/Ccolor/ccompression/cfilter/Cinterlace', $val);
  140. $this->png->width = $val['width'];
  141. $this->png->height = $val['height'];
  142. $this->png->compression = $val['compression'];
  143. }
  144. }
  145. function getNextChunkIdx()
  146. {
  147. return $this->idxStart + CHUNK_SIZE_LENGHT + CHUNK_TYPE_LENGHT + $this->dataLength + CHUNK_CRC_LENGHT;
  148. }
  149. }