diff --git a/src/Site/Lib/Image/Png.php b/src/Site/Lib/Image/Png.php index a8e16da..402ae59 100644 --- a/src/Site/Lib/Image/Png.php +++ b/src/Site/Lib/Image/Png.php @@ -96,7 +96,7 @@ class Png implements ImageInterface { public array $frames = []; private ?array $currentFrame = null; - // TODO: 残り: sBIT, cICP, mDCV, cLLI, iTXt, hIST, sPLT, tIME + // TODO: 残り: sBIT, iTXt, hIST, sPLT, tIME public function parse(string $file): \stdClass { $png = new \stdClass(); $fp = fopen($file, 'rb'); @@ -114,6 +114,8 @@ class Png implements ImageInterface { // N = ビッグエンディアン(4ビット) $this->IHDR = new \stdClass; + $this->IHDR->name = 'IHDR'; + $this->IHDR->length = $length; $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 @@ -154,6 +156,8 @@ next_chunk: } $this->PLTE = new \stdClass; + $this->PLTE->name = 'PLTE'; + $this->PLTE->length = $length; $this->PLTE->palette = $palette; $this->PLTE->crc = $crc; $this->PLTE->crc32 = unpack('N', $crc)[1]; @@ -163,6 +167,8 @@ next_chunk: goto next_chunk; } else if ($nextChunk === "\x74\x52\x4e\x53") { // tRNS $this->tRNS = new \stdClass; + $this->tRNS->name = 'tRNS'; + $this->tRNS->length = $length; $vals = []; $bytes = $this->IHDR->colorType === 2 ? 3 : 1; for ($i = 0; $i < $length; $i += $bytes) { @@ -181,6 +187,8 @@ next_chunk: goto next_chunk; } else if ($nextChunk === "\x63\x48\x52\x4d") { // cHRM $this->cHRM = new \stdClass; + $this->cHRM->name = 'cHRM'; + $this->cHRM->length = $length; $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]; @@ -196,6 +204,8 @@ next_chunk: goto next_chunk; } else if ($nextChunk === "\x67\x41\x4d\x41") { // gAMA $this->gAMA = new \stdClass; + $this->gAMA->name = 'gAMA'; + $this->gAMA->length = $length; $this->gAMA->value = unpack('N', $data)[1]; $this->gAMA->crc = $crc; $this->gAMA->crc32 = unpack('N', $crc)[1]; @@ -204,6 +214,8 @@ next_chunk: goto next_chunk; } else if ($nextChunk === "\x69\x43\x43\x50") { // iCCP $this->iCCP = new \stdClass; + $this->iCCP->name = 'iCCP'; + $this->iCCP->length = $length; $nullPos = strpos($data, "\0"); if ($nullPos === false || $nullPos < 1 || $nullPos > 79) { @@ -235,11 +247,18 @@ iccp_crc: goto next_chunk; } else if ($nextChunk === "\x73\x42\x49\x54") { // sBIT $this->sBIT = new \stdClass; - kys('sBIT'); + $this->sBIT->name = 'sBIT'; + $this->sBIT->length = $length; + kys(['sBIT', $length, bin2hex($data)]); + $this->sBIT->crc = $crc; + $this->sBIT->crc32 = unpack('N', $crc)[1]; + $this->sBIT->crcHex = bin2hex($crc); $png->significantBits = $this->sBIT; goto next_chunk; } else if ($nextChunk === "\x73\x52\x47\x42") { // sRGB $this->sRGB = new \stdClass; + $this->sRGB->name = 'sRGB'; + $this->sRGB->length = $length; $this->sRGB->intent = ord($data); $name = [ 0 => 'Perceptual', @@ -255,34 +274,85 @@ iccp_crc: goto next_chunk; } else if ($nextChunk === "\x63\x49\x43\x50") { // cICP $this->cICP = new \stdClass; - kys('cICP'); + $this->cICP->name = 'cICP'; + $this->cICP->length = $length; + $this->cICP->colorPrimaries = ord($data[0]); + $this->cICP->transferFunction = ord($data[1]); + $this->cICP->matrixCoefficients = ord($data[2]); + $this->cICP->videoFullRangeFlag = ord($data[3]); + $this->cICP->crc = $crc; + $this->cICP->crc32 = unpack('N', $crc)[1]; + $this->cICP->crcHex = bin2hex($crc); $png->videoSignalTypeId = $this->cICP; goto next_chunk; } else if ($nextChunk === "\x6d\x44\x43\x56") { // mDCV $this->mDCV = new \stdClass; - kys('mDCV'); + $this->mDCV->name = 'mDCV'; + $this->mDCV->length = $length; + + $u16 = fn($off) => unpack('n', substr($data, $off, 2))[1]; + $u32 = fn($off) => unpack('N', substr($data, $off, 4))[1]; + $this->mDCV->primaries = [ + 'red' => ['x' => $u16(0), 'y' => $u16(2)], + 'green' => ['x' => $u16(4), 'y' => $u16(6)], + 'blue' => ['x' => $u16(8), 'y' => $u16(10)], + ]; + $this->mDCV->whitePoint = [ + 'x' => $u16(12), + 'y' => $u16(14), + ]; + $this->mDCV->luminance = [ + 'max' => $u16(16) * 0.0001, + 'min' => $u16(20) * 0.0001, + 'maxRaw' => $u16(16), + 'minRaw' => $u16(20), + ]; + $this->mDCV->crc = $crc; + $this->mDCV->crc32 = unpack('N', $crc)[1]; + $this->mDCV->crcHex = bin2hex($crc); $png->masterDisplayColorVolume = $this->mDCV; goto next_chunk; } else if ($nextChunk === "\x63\x4c\x4c\x49") { // cLLI $this->cLLI = new \stdClass; - kys('cLLI'); + $this->cLLI->name = 'cLLI'; + $this->cLLI->length = $length; + $this->cLLI->maxCCL = unpack('N', substr($data, 0, 4))[1] * 0.0001; + $this->cLLI->maxCCLRaw = unpack('N', substr($data, 0, 4))[1]; + $this->cLLI->maxFALL = unpack('N', substr($data, 4, 4))[1] * 0.0001; + $this->cLLI->maxFALLRaw = unpack('N', substr($data, 4, 4))[1]; + $this->cLLI->crc = $crc; + $this->cLLI->crc32 = unpack('N', $crc)[1]; + $this->cLLI->crcHex = bin2hex($crc); $png->contentLightLevelInfo = $this->cLLI; goto next_chunk; } else if ($nextChunk === "\x74\x45\x58\x74") { // tEXt + if (!isset($png->textualData)) $png->textualData = []; $this->tEXt = new \stdClass; + $this->tEXt->name = 'tEXt'; + $this->tEXt->length = $length; $this->tEXt->text = $data; $this->tEXt->crc = $crc; $this->tEXt->crc32 = unpack('N', $crc)[1]; $this->tEXt->crcHex = bin2hex($crc); - $png->textualData = $this->tEXt; + $png->textualData[] = $this->tEXt; goto next_chunk; } else if ($nextChunk === "\x69\x54\x58\x74") { // iTXt + if (!isset($png->internationalTextualData)) $png->textualData = []; $this->iTXt = new \stdClass; - kys('iTXt'); - $png->internationalTextualData = $this->iTXt; + $this->iTXt->name = 'iTXt'; + $this->iTXt->length = $length; + kys(['iTXt', $length, bin2hex($data)]); + $this->iTXt->crc = $crc; + $this->iTXt->crc32 = unpack('N', $crc)[1]; + $this->iTXt->crcHex = bin2hex($crc); + $png->internationalTextualData[] = $this->iTXt; + kys($png); goto next_chunk; } else if ($nextChunk === "\x7a\x54\x58\x74") { // zTXt + if (!isset($png->compressedTextualData)) $png->textualData = []; $this->zTXt = new \stdClass; + $this->zTXt->name = 'zTXt'; + $this->zTXt->length = $length; $nullPos = strpos($data, "\0"); if ($nullPos === false || $nullPos < 1 || $nullPos > 79) { @@ -325,10 +395,12 @@ zTXt_crc: $this->zTXt->crc = $crc; $this->zTXt->crc32 = unpack('N', $crc)[1]; $this->zTXt->crcHex = bin2hex($crc); - $png->compressedTextualData = $this->zTXt; + $png->compressedTextualData[] = $this->zTXt; goto next_chunk; } else if ($nextChunk === "\x62\x4b\x47\x44") { // bKGD $this->bKGD = new \stdClass; + $this->bKGD->name = 'bKGD'; + $this->bKGD->length = $length; 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]); @@ -363,11 +435,19 @@ zTXt_crc: goto next_chunk; } else if ($nextChunk === "\x68\x49\x53\x54") { // hIST $this->hIST = new \stdClass; - kys('hIST'); + $this->hIST->name = 'hIST'; + $this->hIST->length = $length; + kys(['hIST', $length, bin2hex($data)]); + $this->hIST->crc = $crc; + $this->hIST->crc32 = unpack('N', $crc)[1]; + $this->hIST->crcHex = bin2hex($crc); $png->imageHistogram = $this->hIST; + kys($png); goto next_chunk; } else if ($nextChunk === "\x70\x48\x59\x73") { // pHYs $this->pHYs = new \stdClass; + $this->pHYs->name = 'pHYs'; + $this->pHYs->length = $length; $this->pHYs->x = unpack('N', substr($data, 0, 4))[1]; $this->pHYs->y = unpack('N', substr($data, 4, 4))[1]; $unit = [ @@ -383,11 +463,19 @@ zTXt_crc: goto next_chunk; } else if ($nextChunk === "\x73\x50\x4c\x54") { // sPLT $this->sPLT = new \stdClass; - kys('sPLT'); + $this->sPLT->name = 'sPLT'; + $this->sPLT->length = $length; + kys(['sPLT', $length, bin2hex($data)]); + $this->sPLT->crc = $crc; + $this->sPLT->crc32 = unpack('N', $crc)[1]; + $this->sPLT->crcHex = bin2hex($crc); $png->suggestedPalette = $this->sPLT; + kys($png); goto next_chunk; } else if ($nextChunk === "\x65\x58\x49\x66") { // eXIf $this->eXIf = new \stdClass; + $this->eXIf->name = 'eXIf'; + $this->eXIf->length = $length; if (strlen($data) < 8) { $this->eXIf->valid = false; @@ -423,11 +511,19 @@ exif_crc: goto next_chunk; } else if ($nextChunk === "\x74\x49\x4d\x45") { // tIME $this->tIME = new \stdClass; - kys('tIME'); + $this->tIME->name = 'tIME'; + $this->tIME->length = $length; + kys(['tIME', $length, bin2hex($data)]); + $this->tIME->crc = $crc; + $this->tIME->crc32 = unpack('N', $crc)[1]; + $this->tIME->crcHex = bin2hex($crc); $png->lastModificationTime = $this->tIME; + kys($png); goto next_chunk; } else if ($nextChunk === "\x61\x63\x54\x4c") { // acTL $this->acTL = new \stdClass; + $this->acTL->name = 'acTL'; + $this->acTL->length = $length; $this->acTL->numFrames = unpack('N', substr($data, 0, 4))[1]; $this->acTL->numPlays = unpack('N', substr($data, 4, 4))[1]; $this->acTL->crc = $crc; @@ -439,6 +535,8 @@ exif_crc: if ($this->currentFrame !== null) $this->frames[] = (object)$this->currentFrame; $this->currentFrame = [ + 'name' => 'fcTL', + 'length' => $length, 'sequenceNumber' => unpack('N', substr($data, 0, 4))[1], 'width' => unpack('N', substr($data, 4, 4))[1], 'height' => unpack('N', substr($data, 8, 4))[1], @@ -467,6 +565,8 @@ exif_crc: } $compressedData = ($nextChunk === "\x66\x64\x41\x54") ? substr($data, 4) : $data; + $this->currentFrame['name'] = 'fdAT'; + $this->currentFrame['length'] = $length; $this->currentFrame['data'] = $compressedData; $this->currentFrame['dataBase64'] = base64_encode($compressedData); $this->currentFrame['dataHex'] = bin2hex($compressedData); @@ -477,6 +577,7 @@ exif_crc: $this->currentFrame['crcHex'] = bin2hex($crc); if ($nextChunk === "\x49\x44\x41\x54") { + $this->currentFrame['name'] = 'IDAT'; $this->frames[] = (object)$this->currentFrame; $this->currentFrame = null; }