PNGライブラリの延長
This commit is contained in:
@@ -17,7 +17,7 @@ class RGB {
|
||||
$this->r = $r;
|
||||
$this->g = $g;
|
||||
$this->b = $b;
|
||||
$this->rgb = [$r, $g, $b];
|
||||
$this->rgb = ['r' => $r, 'g' => $g, 'b' => $b];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,58 +4,499 @@ namespace Site\Lib\Image;
|
||||
use Site\Lib\Image\ImageInterface;
|
||||
|
||||
class Png implements ImageInterface {
|
||||
public int $width = 0;
|
||||
public int $height = 0;
|
||||
public int $bpp = 0;
|
||||
public int $bitDepth = 0;
|
||||
public int $numColors = 0;
|
||||
public int $colorType = 0;
|
||||
public string $compression = 'NONE';
|
||||
public int $filter = 0;
|
||||
public int $interlace = 0;
|
||||
public bool $hasAlpha = false;
|
||||
public string $imageData = '';
|
||||
public \stdClass $IHDR; // 画像ヘッダー
|
||||
// 横(4バイト)
|
||||
// 丈(4バイト)
|
||||
// ビット深さ(1バイト)
|
||||
// 色類(1バイト)
|
||||
//// | PNG画像タイプ | 色類 | 許可したビット深さ | 解釈 |
|
||||
//// |--------------------------|------|--------------------|----------------------------------------------- |
|
||||
//// | グレースケール | 0 | 1,2,4,8,16 | 各ピクセルはグレースケールのサンプル |
|
||||
//// | トゥルーカラー | 2 | 8,16 | 各ピクセルはR,G,Bトリプル |
|
||||
//// | 指標した色 | 3 | 1,2,4,8 | 各ピクセルはパレット指標(PLTEチャンクあり) |
|
||||
//// | グレースケール+アルファ | 4 | 8,16 | 各ピクセルはグレースケール+アルファのサンプル |
|
||||
//// | トゥルーカラー+アルファ | 6 | 8,16 | 各ピクセルはR,G,B,Aトリプル |
|
||||
// 圧縮関数(1バイト)
|
||||
//// 0 (DEFLATE)しかない
|
||||
// フィルター関数(1バイト)
|
||||
//// 0(アダプティブ・フィルタリング)しかない
|
||||
// インタレース関数(1バイト)
|
||||
//// 0 = なし
|
||||
//// 1 = Adam7
|
||||
public \stdClass $PLTE; // パレット(赤、緑、青)
|
||||
// 色類3 = 必須
|
||||
// 色類2と6 = 任意
|
||||
public \stdClass $IDAT; // 画像データ
|
||||
public \stdClass $IEND; // 画像終了
|
||||
|
||||
public \stdClass $tRNS; // 透明情報
|
||||
// 色類0 = グレーサンプル値(2バイト)
|
||||
// 色類2 = RGBサンプル値(2x2x2バイト)
|
||||
// 色類3 = パレット指標のアルファ(各1バイト)
|
||||
public \stdClass $cHRM; // 主な色度とホワイトポイント
|
||||
// 白X(4バイト)
|
||||
// 白Y(4バイト)
|
||||
// 赤X(4バイト)
|
||||
// 赤Y(4バイト)
|
||||
// 緑X(4バイト)
|
||||
// 緑Y(4バイト)
|
||||
// 青X(4バイト)
|
||||
// 青Y(4バイト)
|
||||
public \stdClass $gAMA; // ガンマ(4バイト)
|
||||
public \stdClass $iCCP; // 組み込みしたICCプロファイル
|
||||
// プロファイル名(1~79バイト)
|
||||
// NULL分離子(1バイト)
|
||||
// 圧縮関数(1バイト)
|
||||
// 圧縮プロファイル(nバイト)
|
||||
public \stdClass $sBIT; // 上位ビット(4バイト)
|
||||
// 色類0 = グレーサンプルビット(1バイト)
|
||||
// 色類2, 3 = RGBサンプルビット(1x1x1バイト)
|
||||
// 色類4 = グレーサンプルビット(1x1バイト)
|
||||
// 色類6 = RGBサンプルビット(1x1x1x1バイト)
|
||||
public \stdClass $sRGB; // レンダリングインテント(1バイト)
|
||||
// 0 = 知覚
|
||||
// 1 = 相対カラー・メトリック
|
||||
// 2 = 彩度
|
||||
// 3 = 絶対カラー・メトリック
|
||||
public \stdClass $cICP; // (1バイト)
|
||||
public \stdClass $mDCV; // (1バイト)
|
||||
public \stdClass $cLLI; // (1バイト)
|
||||
public \stdClass $tEXt; // 文字データ
|
||||
public \stdClass $iTXt; // 圧縮した文字データ
|
||||
public \stdClass $zTXt; // 国際文字データ
|
||||
public \stdClass $bKGD; // 拝啓色
|
||||
public \stdClass $hIST; // 画像ヒストグラム
|
||||
public \stdClass $pHYs; // 物理的なぷくセルのサイズ
|
||||
// 単位当たりのピクセル数(PPU)のX軸(4バイト)
|
||||
// 単位当たりのピクセル数(PPU)のY軸(4バイト)
|
||||
// ユニット指定子(1バイト)
|
||||
//// 0 = 不明
|
||||
//// 1 = メートル
|
||||
public \stdClass $sPLT; // 提案されている配色パターン
|
||||
// パレット名(1~79バイト)
|
||||
// NULL分離子(1バイト)
|
||||
// 赤(1か2バイト)
|
||||
// 緑(1か2バイト)
|
||||
// 青(1か2バイト)
|
||||
// アルファ(1か2バイト)
|
||||
// 周波数(2バイト)
|
||||
// 等
|
||||
public \stdClass $eXIf; // Exifプロファイル
|
||||
public \stdClass $tIME; // 画像の最後変更日
|
||||
// 年(2バイト)
|
||||
// 月(1バイト:1-12)
|
||||
// 日(1バイト:1-31)
|
||||
// 時(1バイト:0-23)
|
||||
// 分(1バイト:0-59)
|
||||
// 秒(1バイト:0-60)
|
||||
public \stdClass $acTL; // アニメーション・コントロール
|
||||
// フレーム数(4バイト)
|
||||
// 再生数(4バイト、0 = 無限)
|
||||
|
||||
public array $frames = [];
|
||||
private ?array $currentFrame = null;
|
||||
|
||||
// TODO: 残り: sBIT, cICP, mDCV, cLLI, iTXt, hIST, sPLT, tIME
|
||||
public function parse(string $file): \stdClass {
|
||||
$png = new \stdClass();
|
||||
$fp = fopen($file, 'rb');
|
||||
|
||||
fread($fp, 8); // マジックのスキップ
|
||||
unpack('N', fread($fp, 4))[1];
|
||||
$type = fread($fp, 4);
|
||||
if ($type !== 'IHDR') {
|
||||
$magic = bin2hex(fread($fp, 8)); // マジックのスキップ
|
||||
$length = unpack('N', fread($fp, 4))[1];
|
||||
$header = fread($fp, 4);
|
||||
if ($header !== 'IHDR') {
|
||||
fclose($fp);
|
||||
return $png;
|
||||
}
|
||||
|
||||
// IHDRデータ
|
||||
$data = fread($fp, 13);
|
||||
$data = fread($fp, $length);
|
||||
|
||||
// N = ビッグエンディアン(4ビット)
|
||||
$this->width = unpack('N', substr($data, 0, 4))[1];
|
||||
$this->height = unpack('N', substr($data, 4, 4))[1];
|
||||
$this->bitDepth = ord($data[8]);
|
||||
$this->numColors = 1 << $this->bitDepth;
|
||||
$this->colorType = ord($data[9]);
|
||||
$this->compression = 'DEFLATE';
|
||||
$this->filter = ord($data[11]);
|
||||
$this->interlace = ord($data[12]); // 0 = なし, 1 = Adam7
|
||||
$this->bpp = $this->bitDepth * $this->getSamplesPerPixel($this->colorType);
|
||||
$this->hasAlpha = ($this->colorType === 4 || $this->colorType === 6);
|
||||
$this->IHDR = new \stdClass;
|
||||
$this->IHDR->width = unpack('N', substr($data, 0, 4))[1];
|
||||
$this->IHDR->height = unpack('N', substr($data, 4, 4))[1];
|
||||
$this->IHDR->bitDepth = ord($data[8]); // 1, 2, 4, 8, 16
|
||||
$this->IHDR->numColors = 1 << $this->IHDR->bitDepth;
|
||||
$this->IHDR->colorType = ord($data[9]); // 0, 2, 3, 4, 6
|
||||
$this->IHDR->compressionType = ord($data[10]);
|
||||
$this->IHDR->compression = 'DEFLATE';
|
||||
$this->IHDR->filter = ord($data[11]);
|
||||
$this->IHDR->interlace = ord($data[12]); // 0 = なし, 1 = Adam7
|
||||
$this->IHDR->bpp = $this->IHDR->bitDepth * $this->getSamplesPerPixel($this->IHDR->colorType);
|
||||
$this->IHDR->hasAlpha = ($this->IHDR->colorType === 4 || $this->IHDR->colorType === 6);
|
||||
$crc = fread($fp, 4);
|
||||
$this->IHDR->crc = $crc;
|
||||
$this->IHDR->crc32 = unpack('N', $crc)[1];
|
||||
$this->IHDR->crcHex = bin2hex($crc);
|
||||
|
||||
$png->width = $this->width;
|
||||
$png->height = $this->height;
|
||||
$png->bitDepth = $this->bitDepth;
|
||||
$png->numColors = $this->numColors;
|
||||
$png->colorType = $this->colorType;
|
||||
$png->compression = $this->compression;
|
||||
$png->filter = $this->filter;
|
||||
$png->interlace = $this->interlace;
|
||||
$png->bpp = $this->bpp;
|
||||
$png->hasAlpha = $this->hasAlpha;
|
||||
$png->header = $this->IHDR;
|
||||
|
||||
next_chunk:
|
||||
$lengthBytes = fread($fp, 4);
|
||||
if (strlen($lengthBytes) < 4) {
|
||||
logger(LogType::Image, 'バイト長さが悪い。ファイル:'.$file.'、バイト長さ:'.'$lengthBytes');
|
||||
fclose($fp);
|
||||
return $png;
|
||||
}
|
||||
|
||||
$length = unpack('N', $lengthBytes)[1];
|
||||
$nextChunk = fread($fp, 4);
|
||||
$data = $length > 0 ? fread($fp, $length) : '';
|
||||
$crc = fread($fp, 4);
|
||||
|
||||
// PLTEデータ
|
||||
if ($nextChunk === "\x50\x4c\x54\x45") { // PLTE
|
||||
$palette = [];
|
||||
for ($i = 0; $i < $length; $i += 3) {
|
||||
$colors = new \Site\Lib\RGB(ord($data[$i]), ord($data[$i+1]), ord($data[$i+2]));
|
||||
$palette[] = $colors->rgb;
|
||||
}
|
||||
|
||||
$this->PLTE = new \stdClass;
|
||||
$this->PLTE->palette = $palette;
|
||||
$this->PLTE->crc = $crc;
|
||||
$this->PLTE->crc32 = unpack('N', $crc)[1];
|
||||
$this->PLTE->crcHex = bin2hex($crc);
|
||||
|
||||
$png->palette = $this->PLTE;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x74\x52\x4e\x53") { // tRNS
|
||||
$this->tRNS = new \stdClass;
|
||||
$vals = [];
|
||||
$bytes = $this->IHDR->colorType === 2 ? 3 : 1;
|
||||
for ($i = 0; $i < $length; $i += $bytes) {
|
||||
if ($this->IHDR->colorType === 2) {
|
||||
$colors = new \Site\Lib\RGB(ord($data[$i]), ord($data[$i+1]), ord($data[$i+2]));
|
||||
$vals[] = $colors->rgb;
|
||||
} else {
|
||||
$vals[] = ord($data[$i]);
|
||||
}
|
||||
}
|
||||
$this->tRNS->values = $vals;
|
||||
$this->tRNS->crc = $crc;
|
||||
$this->tRNS->crc32 = unpack('N', $crc)[1];
|
||||
$this->tRNS->crcHex = bin2hex($crc);
|
||||
$png->transparency = $this->tRNS;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x63\x48\x52\x4d") { // cHRM
|
||||
$this->cHRM = new \stdClass;
|
||||
$this->cHRM->whitePointX = unpack('N', substr($data, 0, 4))[1];
|
||||
$this->cHRM->whitePointY = unpack('N', substr($data, 4, 4))[1];
|
||||
$this->cHRM->redX = unpack('N', substr($data, 8, 4))[1];
|
||||
$this->cHRM->redY = unpack('N', substr($data, 12, 4))[1];
|
||||
$this->cHRM->greenX = unpack('N', substr($data, 16, 4))[1];
|
||||
$this->cHRM->greenY = unpack('N', substr($data, 20, 4))[1];
|
||||
$this->cHRM->blueX = unpack('N', substr($data, 24, 4))[1];
|
||||
$this->cHRM->blueY = unpack('N', substr($data, 28, 4))[1];
|
||||
$this->cHRM->crc = $crc;
|
||||
$this->cHRM->crc32 = unpack('N', $crc)[1];
|
||||
$this->cHRM->crcHex = bin2hex($crc);
|
||||
$png->chromaticities = $this->cHRM;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x67\x41\x4d\x41") { // gAMA
|
||||
$this->gAMA = new \stdClass;
|
||||
$this->gAMA->value = unpack('N', $data)[1];
|
||||
$this->gAMA->crc = $crc;
|
||||
$this->gAMA->crc32 = unpack('N', $crc)[1];
|
||||
$this->gAMA->crcHex = bin2hex($crc);
|
||||
$png->gamma = $this->gAMA;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x69\x43\x43\x50") { // iCCP
|
||||
$this->iCCP = new \stdClass;
|
||||
|
||||
$nullPos = strpos($data, "\0");
|
||||
if ($nullPos === false || $nullPos < 1 || $nullPos > 79) {
|
||||
$this->iCCP->valid = false;
|
||||
$this->iCCP->error = "Missing or invalid profile name terminator";
|
||||
goto iccp_crc;
|
||||
}
|
||||
|
||||
$this->iCCP->profileName = substr($data, 0, $nullPos);
|
||||
$compressionMethod = ord($data[$nullPos + 1]);
|
||||
$this->iCCP->compressionMethod = $compressionMethod;
|
||||
|
||||
if ($compressionMethod !== 0) {
|
||||
$this->iCCP->valid = false;
|
||||
$this->iCCP->error = "Unsupported compression method: {$compressionMethod}";
|
||||
goto iccp_crc;
|
||||
}
|
||||
|
||||
$compressedData = substr($data, $nullPos + 2);
|
||||
$this->iCCP->compressedData = $compressedData;
|
||||
$this->iCCP->compressedSize = strlen($compressedData);
|
||||
$this->iCCP->valid = true;
|
||||
|
||||
iccp_crc:
|
||||
$this->iCCP->crc = $crc;
|
||||
$this->iCCP->crc32 = unpack('N', $crc)[1];
|
||||
$this->iCCP->crcHex = bin2hex($crc);
|
||||
$png->iccProfile = $this->iCCP;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x73\x42\x49\x54") { // sBIT
|
||||
$this->sBIT = new \stdClass;
|
||||
kys('sBIT');
|
||||
$png->significantBits = $this->sBIT;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x73\x52\x47\x42") { // sRGB
|
||||
$this->sRGB = new \stdClass;
|
||||
$this->sRGB->intent = ord($data);
|
||||
$name = [
|
||||
0 => 'Perceptual',
|
||||
1 => 'Relative colorimetric',
|
||||
2 => 'Saturation',
|
||||
3 => 'Absolute colorimetric',
|
||||
];
|
||||
$this->sRGB->intentName = $name[$this->sRGB->intent];
|
||||
$this->sRGB->crc = $crc;
|
||||
$this->sRGB->crc32 = unpack('N', $crc)[1];
|
||||
$this->sRGB->crcHex = bin2hex($crc);
|
||||
$png->rgbColorSpace = $this->sRGB;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x63\x49\x43\x50") { // cICP
|
||||
$this->cICP = new \stdClass;
|
||||
kys('cICP');
|
||||
$png->videoSignalTypeId = $this->cICP;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x6d\x44\x43\x56") { // mDCV
|
||||
$this->mDCV = new \stdClass;
|
||||
kys('mDCV');
|
||||
$png->masterDisplayColorVolume = $this->mDCV;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x63\x4c\x4c\x49") { // cLLI
|
||||
$this->cLLI = new \stdClass;
|
||||
kys('cLLI');
|
||||
$png->contentLightLevelInfo = $this->cLLI;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x74\x45\x58\x74") { // tEXt
|
||||
$this->tEXt = new \stdClass;
|
||||
$this->tEXt->text = $data;
|
||||
$this->tEXt->crc = $crc;
|
||||
$this->tEXt->crc32 = unpack('N', $crc)[1];
|
||||
$this->tEXt->crcHex = bin2hex($crc);
|
||||
$png->textualData = $this->tEXt;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x69\x54\x58\x74") { // iTXt
|
||||
$this->iTXt = new \stdClass;
|
||||
kys('iTXt');
|
||||
$png->internationalTextualData = $this->iTXt;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x7a\x54\x58\x74") { // zTXt
|
||||
$this->zTXt = new \stdClass;
|
||||
|
||||
$nullPos = strpos($data, "\0");
|
||||
if ($nullPos === false || $nullPos < 1 || $nullPos > 79) {
|
||||
$this->zTXt->valid = false;
|
||||
$this->zTXt->error = "Missing or invalid profile name terminator";
|
||||
goto zTXt_crc;
|
||||
}
|
||||
|
||||
$this->zTXt->keyword = rtrim(substr($data, 0, $nullPos));
|
||||
$compressionMethod = ord($data[$nullPos + 1] ?? "\xFF");
|
||||
|
||||
$this->zTXt->compressionMethod = $compressionMethod;
|
||||
|
||||
if ($compressionMethod !== 0) {
|
||||
$this->zTXt->valid = false;
|
||||
$this->zTXt->error = "Unsupported compression method";
|
||||
goto zTXt_crc;
|
||||
}
|
||||
|
||||
$compressedText = substr($data, $nullPos + 2);
|
||||
fread($fp, 13); // ????????????????????????
|
||||
$this->zTXt->compressedText = $compressedText;
|
||||
$this->zTXt->compressedLength = strlen($compressedText);
|
||||
|
||||
$text = zlib_decode($compressedText);
|
||||
|
||||
if ($text === false) {
|
||||
$this->zTXt->valid = false;
|
||||
$this->zTXt->decompressionError = true;
|
||||
$this->zTXt->text = null;
|
||||
goto zTXt_crc;
|
||||
} else {
|
||||
$this->zTXt->text = $text;
|
||||
$this->zTXt->textUtf8 = mb_convert_encoding($text, 'UTF-8', 'ISO-8859-1');
|
||||
}
|
||||
|
||||
$this->zTXt->valid = true;
|
||||
|
||||
zTXt_crc:
|
||||
$this->zTXt->crc = $crc;
|
||||
$this->zTXt->crc32 = unpack('N', $crc)[1];
|
||||
$this->zTXt->crcHex = bin2hex($crc);
|
||||
$png->compressedTextualData = $this->zTXt;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x62\x4b\x47\x44") { // bKGD
|
||||
$this->bKGD = new \stdClass;
|
||||
if ($this->IHDR->colorType === 2 || $this->IHDR->colorType === 6) {
|
||||
$r = (ord($data[0]) << 8) | ord($data[1]);
|
||||
$g = (ord($data[2]) << 8) | ord($data[3]);
|
||||
$b = (ord($data[4]) << 8) | ord($data[5]);
|
||||
|
||||
$this->bKGD->type = 'rgb16';
|
||||
$this->bKGD->r = $r;
|
||||
$this->bKGD->g = $g;
|
||||
$this->bKGD->b = $b;
|
||||
|
||||
$this->bKGD->r8 = $r >> 8;
|
||||
$this->bKGD->g8 = $g >> 8;
|
||||
$this->bKGD->b8 = $b >> 8;
|
||||
$this->bKGD->webColor = sprintf("#%02x%02x%02x", $this->bKGD->r8, $this->bKGD->g8, $this->bKGD->b8);
|
||||
} else if ($this->IHDR->colorType === 2 || $this->IHDR->colorType === 6) {
|
||||
$gray = (ord($data[0]) << 8) | ord($data[1]);
|
||||
$this->bKGD->type = 'gray16';
|
||||
$this->bKGD->gray = $gray;
|
||||
$this->bKGD->gray8 = $gray >> 8;
|
||||
} else if ($this->IHDR->colorType === 3) {
|
||||
$this->bKGD->type = 'palette_index';
|
||||
$this->bKGD->value = ord($data);
|
||||
} else {
|
||||
$this->bKGD->type = 'unknown';
|
||||
$this->bKGD->error = 'Invalid color type for bKGD';
|
||||
}
|
||||
|
||||
$this->bKGD->crc = $crc;
|
||||
$this->bKGD->crc32 = unpack('N', $crc)[1];
|
||||
$this->bKGD->crcHex = bin2hex($crc);
|
||||
$png->backgroundColor = $this->bKGD;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x68\x49\x53\x54") { // hIST
|
||||
$this->hIST = new \stdClass;
|
||||
kys('hIST');
|
||||
$png->imageHistogram = $this->hIST;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x70\x48\x59\x73") { // pHYs
|
||||
$this->pHYs = new \stdClass;
|
||||
$this->pHYs->x = unpack('N', substr($data, 0, 4))[1];
|
||||
$this->pHYs->y = unpack('N', substr($data, 4, 4))[1];
|
||||
$unit = [
|
||||
0 => 'unit is unknown',
|
||||
1 => 'unit is the metre',
|
||||
];
|
||||
$this->pHYs->unitId = ord($data[8]);
|
||||
$this->pHYs->unit = $unit[$this->pHYs->unitId];
|
||||
$this->pHYs->crc = $crc;
|
||||
$this->pHYs->crc32 = unpack('N', $crc)[1];
|
||||
$this->pHYs->crcHex = bin2hex($crc);
|
||||
$png->physicalPixelDimensions = $this->pHYs;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x73\x50\x4c\x54") { // sPLT
|
||||
$this->sPLT = new \stdClass;
|
||||
kys('sPLT');
|
||||
$png->suggestedPalette = $this->sPLT;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x65\x58\x49\x66") { // eXIf
|
||||
$this->eXIf = new \stdClass;
|
||||
|
||||
if (strlen($data) < 8) {
|
||||
$this->eXIf->valid = false;
|
||||
$this->eXIf->error = "eXIf chunk too short";
|
||||
goto exif_crc;
|
||||
}
|
||||
|
||||
$byteOrder = substr($data, 0, 2);
|
||||
$magic = substr($data, 2, 2);
|
||||
|
||||
$this->eXIf->rawData = $data;
|
||||
$this->eXIf->rawLength = $length;
|
||||
$this->eXIf->byteOrder = $byteOrder; // II = リトルエンディアン、MM = ビッグエンディアン
|
||||
$this->eXIf->isBigEndian = ($byteOrder === 'MM');
|
||||
$this->eXIf->magic = unpack('n', $magic)[1]; // いつでも42ではず
|
||||
|
||||
if (strlen($data) < 8) {
|
||||
$this->eXIf->valid = false;
|
||||
$this->eXIf->error = "Invalid TIFF magic number";
|
||||
goto exif_crc;
|
||||
}
|
||||
|
||||
$this->eXIf->valid = true;
|
||||
|
||||
$ifdOffset = unpack($this->eXIf->isBigEndian ? 'N' : 'V', substr($data, 4, 4))[1];
|
||||
$this->eXIf->firstIfdOffset = $ifdOffset;
|
||||
|
||||
exif_crc:
|
||||
$this->eXIf->crc = $crc;
|
||||
$this->eXIf->crc32 = unpack('N', $crc)[1];
|
||||
$this->eXIf->crcHex = bin2hex($crc);
|
||||
$png->exifProfile = $this->eXIf;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x74\x49\x4d\x45") { // tIME
|
||||
$this->tIME = new \stdClass;
|
||||
kys('tIME');
|
||||
$png->lastModificationTime = $this->tIME;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x61\x63\x54\x4c") { // acTL
|
||||
$this->acTL = new \stdClass;
|
||||
$this->acTL->numFrames = unpack('N', substr($data, 0, 4))[1];
|
||||
$this->acTL->numPlays = unpack('N', substr($data, 4, 4))[1];
|
||||
$this->acTL->crc = $crc;
|
||||
$this->acTL->crc32 = unpack('N', $crc)[1];
|
||||
$this->acTL->crcHex = bin2hex($crc);
|
||||
$png->aniationControl = $this->acTL;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x66\x63\x54\x4c") { // fcTL
|
||||
if ($this->currentFrame !== null) $this->frames[] = (object)$this->currentFrame;
|
||||
|
||||
$this->currentFrame = [
|
||||
'sequenceNumber' => unpack('N', substr($data, 0, 4))[1],
|
||||
'width' => unpack('N', substr($data, 4, 4))[1],
|
||||
'height' => unpack('N', substr($data, 8, 4))[1],
|
||||
'xOffset' => unpack('N', substr($data, 12, 4))[1],
|
||||
'yOffset' => unpack('N', substr($data, 16, 4))[1],
|
||||
'delayNum' => unpack('n', substr($data, 20, 2))[1],
|
||||
'delayDen' => unpack('n', substr($data, 22, 2))[1],
|
||||
'disposeOp' => ord($data[24]),
|
||||
'blendOp' => ord($data[25]),
|
||||
'data' => null,
|
||||
'crc' => $crc,
|
||||
'crc32' => unpack('N', $crc)[1],
|
||||
'crcHex' => bin2hex($crc),
|
||||
];
|
||||
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x49\x44\x41\x54" || $nextChunk === "\x66\x64\x41\x54") { // IDAT || fdAT
|
||||
if ($this->currentFrame === null) {
|
||||
$this->currentFrame = [
|
||||
'sequenceNumber' => 0,
|
||||
'isFirstFrame' => true,
|
||||
'width' => $this->IHDR->width,
|
||||
'height' => $this->IHDR->height,
|
||||
'data' => null,
|
||||
];
|
||||
}
|
||||
|
||||
$compressedData = ($nextChunk === "\x66\x64\x41\x54") ? substr($data, 4) : $data;
|
||||
$this->currentFrame['data'] = $compressedData;
|
||||
$this->currentFrame['dataBase64'] = base64_encode($compressedData);
|
||||
$this->currentFrame['dataHex'] = bin2hex($compressedData);
|
||||
$this->currentFrame['dataLength'] = strlen($compressedData);
|
||||
$this->currentFrame['dataChunkType'] = $nextChunk;
|
||||
$this->currentFrame['crc'] = $crc;
|
||||
$this->currentFrame['crcBase64'] = unpack('N', $crc)[1];
|
||||
$this->currentFrame['crcHex'] = bin2hex($crc);
|
||||
|
||||
if ($nextChunk === "\x49\x44\x41\x54") {
|
||||
$this->frames[] = (object)$this->currentFrame;
|
||||
$this->currentFrame = null;
|
||||
}
|
||||
|
||||
$png->data = $this->frames;
|
||||
goto next_chunk;
|
||||
} else if ($nextChunk === "\x49\x45\x4e\x44") { // IEND
|
||||
if ($this->currentFrame !== null) {
|
||||
$this->frames[] = (object)$this->currentFrame;
|
||||
}
|
||||
$png->end = (object)['crc' => $crc, 'crc32' => unpack('N', $crc)[1], 'crcHex' => bin2hex($crc)];
|
||||
} else {
|
||||
fclose($fp);
|
||||
$err = '不明なチャンク:'.$nextChunk.'、HEX:'.bin2hex($nextChunk);
|
||||
kys($err);
|
||||
logger(\LogType::Image, '【PNG】'.$err);
|
||||
return $png;
|
||||
}
|
||||
|
||||
fclose($fp);
|
||||
|
||||
return $png;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user