もっと
諏訪子 <suwako@076.moe>、2026-01-13 02:17
コミット 2a0956dfd4202b0930b520ba9effb291cd6df3c0
・親 6582737
・ファイルを見る
1個のファイル、+113 -12
| @@ -96,7 +96,7 @@ | ||
| 96 | 96 | public array $frames = []; |
| 97 | 97 | private ?array $currentFrame = null; |
| 98 | 98 | |
| 99 | - // TODO: 残り: sBIT, cICP, mDCV, cLLI, iTXt, hIST, sPLT, tIME | |
| 99 | + // TODO: 残り: sBIT, iTXt, hIST, sPLT, tIME | |
| 100 | 100 | public function parse(string $file): \stdClass { |
| 101 | 101 | $png = new \stdClass(); |
| 102 | 102 | $fp = fopen($file, 'rb'); |
| @@ -114,6 +114,8 @@ | ||
| 114 | 114 | |
| 115 | 115 | // N = ビッグエンディアン(4ビット) |
| 116 | 116 | $this->IHDR = new \stdClass; |
| 117 | + $this->IHDR->name = 'IHDR'; | |
| 118 | + $this->IHDR->length = $length; | |
| 117 | 119 | $this->IHDR->width = unpack('N', substr($data, 0, 4))[1]; |
| 118 | 120 | $this->IHDR->height = unpack('N', substr($data, 4, 4))[1]; |
| 119 | 121 | $this->IHDR->bitDepth = ord($data[8]); // 1, 2, 4, 8, 16 |
| @@ -154,6 +156,8 @@ | ||
| 154 | 156 | } |
| 155 | 157 | |
| 156 | 158 | $this->PLTE = new \stdClass; |
| 159 | + $this->PLTE->name = 'PLTE'; | |
| 160 | + $this->PLTE->length = $length; | |
| 157 | 161 | $this->PLTE->palette = $palette; |
| 158 | 162 | $this->PLTE->crc = $crc; |
| 159 | 163 | $this->PLTE->crc32 = unpack('N', $crc)[1]; |
| @@ -163,6 +167,8 @@ | ||
| 163 | 167 | goto next_chunk; |
| 164 | 168 | } else if ($nextChunk === "\x74\x52\x4e\x53") { // tRNS |
| 165 | 169 | $this->tRNS = new \stdClass; |
| 170 | + $this->tRNS->name = 'tRNS'; | |
| 171 | + $this->tRNS->length = $length; | |
| 166 | 172 | $vals = []; |
| 167 | 173 | $bytes = $this->IHDR->colorType === 2 ? 3 : 1; |
| 168 | 174 | for ($i = 0; $i < $length; $i += $bytes) { |
| @@ -181,6 +187,8 @@ | ||
| 181 | 187 | goto next_chunk; |
| 182 | 188 | } else if ($nextChunk === "\x63\x48\x52\x4d") { // cHRM |
| 183 | 189 | $this->cHRM = new \stdClass; |
| 190 | + $this->cHRM->name = 'cHRM'; | |
| 191 | + $this->cHRM->length = $length; | |
| 184 | 192 | $this->cHRM->whitePointX = unpack('N', substr($data, 0, 4))[1]; |
| 185 | 193 | $this->cHRM->whitePointY = unpack('N', substr($data, 4, 4))[1]; |
| 186 | 194 | $this->cHRM->redX = unpack('N', substr($data, 8, 4))[1]; |
| @@ -196,6 +204,8 @@ | ||
| 196 | 204 | goto next_chunk; |
| 197 | 205 | } else if ($nextChunk === "\x67\x41\x4d\x41") { // gAMA |
| 198 | 206 | $this->gAMA = new \stdClass; |
| 207 | + $this->gAMA->name = 'gAMA'; | |
| 208 | + $this->gAMA->length = $length; | |
| 199 | 209 | $this->gAMA->value = unpack('N', $data)[1]; |
| 200 | 210 | $this->gAMA->crc = $crc; |
| 201 | 211 | $this->gAMA->crc32 = unpack('N', $crc)[1]; |
| @@ -204,6 +214,8 @@ | ||
| 204 | 214 | goto next_chunk; |
| 205 | 215 | } else if ($nextChunk === "\x69\x43\x43\x50") { // iCCP |
| 206 | 216 | $this->iCCP = new \stdClass; |
| 217 | + $this->iCCP->name = 'iCCP'; | |
| 218 | + $this->iCCP->length = $length; | |
| 207 | 219 | |
| 208 | 220 | $nullPos = strpos($data, "\0"); |
| 209 | 221 | if ($nullPos === false || $nullPos < 1 || $nullPos > 79) { |
| @@ -235,11 +247,18 @@ | ||
| 235 | 247 | goto next_chunk; |
| 236 | 248 | } else if ($nextChunk === "\x73\x42\x49\x54") { // sBIT |
| 237 | 249 | $this->sBIT = new \stdClass; |
| 238 | - kys('sBIT'); | |
| 250 | + $this->sBIT->name = 'sBIT'; | |
| 251 | + $this->sBIT->length = $length; | |
| 252 | + kys(['sBIT', $length, bin2hex($data)]); | |
| 253 | + $this->sBIT->crc = $crc; | |
| 254 | + $this->sBIT->crc32 = unpack('N', $crc)[1]; | |
| 255 | + $this->sBIT->crcHex = bin2hex($crc); | |
| 239 | 256 | $png->significantBits = $this->sBIT; |
| 240 | 257 | goto next_chunk; |
| 241 | 258 | } else if ($nextChunk === "\x73\x52\x47\x42") { // sRGB |
| 242 | 259 | $this->sRGB = new \stdClass; |
| 260 | + $this->sRGB->name = 'sRGB'; | |
| 261 | + $this->sRGB->length = $length; | |
| 243 | 262 | $this->sRGB->intent = ord($data); |
| 244 | 263 | $name = [ |
| 245 | 264 | 0 => 'Perceptual', |
| @@ -255,34 +274,85 @@ | ||
| 255 | 274 | goto next_chunk; |
| 256 | 275 | } else if ($nextChunk === "\x63\x49\x43\x50") { // cICP |
| 257 | 276 | $this->cICP = new \stdClass; |
| 258 | - kys('cICP'); | |
| 277 | + $this->cICP->name = 'cICP'; | |
| 278 | + $this->cICP->length = $length; | |
| 279 | + $this->cICP->colorPrimaries = ord($data[0]); | |
| 280 | + $this->cICP->transferFunction = ord($data[1]); | |
| 281 | + $this->cICP->matrixCoefficients = ord($data[2]); | |
| 282 | + $this->cICP->videoFullRangeFlag = ord($data[3]); | |
| 283 | + $this->cICP->crc = $crc; | |
| 284 | + $this->cICP->crc32 = unpack('N', $crc)[1]; | |
| 285 | + $this->cICP->crcHex = bin2hex($crc); | |
| 259 | 286 | $png->videoSignalTypeId = $this->cICP; |
| 260 | 287 | goto next_chunk; |
| 261 | 288 | } else if ($nextChunk === "\x6d\x44\x43\x56") { // mDCV |
| 262 | 289 | $this->mDCV = new \stdClass; |
| 263 | - kys('mDCV'); | |
| 290 | + $this->mDCV->name = 'mDCV'; | |
| 291 | + $this->mDCV->length = $length; | |
| 292 | + | |
| 293 | + $u16 = fn($off) => unpack('n', substr($data, $off, 2))[1]; | |
| 294 | + $u32 = fn($off) => unpack('N', substr($data, $off, 4))[1]; | |
| 295 | + $this->mDCV->primaries = [ | |
| 296 | + 'red' => ['x' => $u16(0), 'y' => $u16(2)], | |
| 297 | + 'green' => ['x' => $u16(4), 'y' => $u16(6)], | |
| 298 | + 'blue' => ['x' => $u16(8), 'y' => $u16(10)], | |
| 299 | + ]; | |
| 300 | + $this->mDCV->whitePoint = [ | |
| 301 | + 'x' => $u16(12), | |
| 302 | + 'y' => $u16(14), | |
| 303 | + ]; | |
| 304 | + $this->mDCV->luminance = [ | |
| 305 | + 'max' => $u16(16) * 0.0001, | |
| 306 | + 'min' => $u16(20) * 0.0001, | |
| 307 | + 'maxRaw' => $u16(16), | |
| 308 | + 'minRaw' => $u16(20), | |
| 309 | + ]; | |
| 310 | + $this->mDCV->crc = $crc; | |
| 311 | + $this->mDCV->crc32 = unpack('N', $crc)[1]; | |
| 312 | + $this->mDCV->crcHex = bin2hex($crc); | |
| 264 | 313 | $png->masterDisplayColorVolume = $this->mDCV; |
| 265 | 314 | goto next_chunk; |
| 266 | 315 | } else if ($nextChunk === "\x63\x4c\x4c\x49") { // cLLI |
| 267 | 316 | $this->cLLI = new \stdClass; |
| 268 | - kys('cLLI'); | |
| 317 | + $this->cLLI->name = 'cLLI'; | |
| 318 | + $this->cLLI->length = $length; | |
| 319 | + $this->cLLI->maxCCL = unpack('N', substr($data, 0, 4))[1] * 0.0001; | |
| 320 | + $this->cLLI->maxCCLRaw = unpack('N', substr($data, 0, 4))[1]; | |
| 321 | + $this->cLLI->maxFALL = unpack('N', substr($data, 4, 4))[1] * 0.0001; | |
| 322 | + $this->cLLI->maxFALLRaw = unpack('N', substr($data, 4, 4))[1]; | |
| 323 | + $this->cLLI->crc = $crc; | |
| 324 | + $this->cLLI->crc32 = unpack('N', $crc)[1]; | |
| 325 | + $this->cLLI->crcHex = bin2hex($crc); | |
| 269 | 326 | $png->contentLightLevelInfo = $this->cLLI; |
| 270 | 327 | goto next_chunk; |
| 271 | 328 | } else if ($nextChunk === "\x74\x45\x58\x74") { // tEXt |
| 329 | + if (!isset($png->textualData)) $png->textualData = []; | |
| 272 | 330 | $this->tEXt = new \stdClass; |
| 331 | + $this->tEXt->name = 'tEXt'; | |
| 332 | + $this->tEXt->length = $length; | |
| 273 | 333 | $this->tEXt->text = $data; |
| 274 | 334 | $this->tEXt->crc = $crc; |
| 275 | 335 | $this->tEXt->crc32 = unpack('N', $crc)[1]; |
| 276 | 336 | $this->tEXt->crcHex = bin2hex($crc); |
| 277 | - $png->textualData = $this->tEXt; | |
| 337 | + $png->textualData[] = $this->tEXt; | |
| 278 | 338 | goto next_chunk; |
| 279 | 339 | } else if ($nextChunk === "\x69\x54\x58\x74") { // iTXt |
| 340 | + if (!isset($png->internationalTextualData)) $png->textualData = []; | |
| 280 | 341 | $this->iTXt = new \stdClass; |
| 281 | - kys('iTXt'); | |
| 282 | - $png->internationalTextualData = $this->iTXt; | |
| 342 | + $this->iTXt->name = 'iTXt'; | |
| 343 | + $this->iTXt->length = $length; | |
| 344 | + kys(['iTXt', $length, bin2hex($data)]); | |
| 345 | + $this->iTXt->crc = $crc; | |
| 346 | + $this->iTXt->crc32 = unpack('N', $crc)[1]; | |
| 347 | + $this->iTXt->crcHex = bin2hex($crc); | |
| 348 | + $png->internationalTextualData[] = $this->iTXt; | |
| 349 | + kys($png); | |
| 283 | 350 | goto next_chunk; |
| 284 | 351 | } else if ($nextChunk === "\x7a\x54\x58\x74") { // zTXt |
| 352 | + if (!isset($png->compressedTextualData)) $png->textualData = []; | |
| 285 | 353 | $this->zTXt = new \stdClass; |
| 354 | + $this->zTXt->name = 'zTXt'; | |
| 355 | + $this->zTXt->length = $length; | |
| 286 | 356 | |
| 287 | 357 | $nullPos = strpos($data, "\0"); |
| 288 | 358 | if ($nullPos === false || $nullPos < 1 || $nullPos > 79) { |
| @@ -325,10 +395,12 @@ | ||
| 325 | 395 | $this->zTXt->crc = $crc; |
| 326 | 396 | $this->zTXt->crc32 = unpack('N', $crc)[1]; |
| 327 | 397 | $this->zTXt->crcHex = bin2hex($crc); |
| 328 | - $png->compressedTextualData = $this->zTXt; | |
| 398 | + $png->compressedTextualData[] = $this->zTXt; | |
| 329 | 399 | goto next_chunk; |
| 330 | 400 | } else if ($nextChunk === "\x62\x4b\x47\x44") { // bKGD |
| 331 | 401 | $this->bKGD = new \stdClass; |
| 402 | + $this->bKGD->name = 'bKGD'; | |
| 403 | + $this->bKGD->length = $length; | |
| 332 | 404 | if ($this->IHDR->colorType === 2 || $this->IHDR->colorType === 6) { |
| 333 | 405 | $r = (ord($data[0]) << 8) | ord($data[1]); |
| 334 | 406 | $g = (ord($data[2]) << 8) | ord($data[3]); |
| @@ -363,11 +435,19 @@ | ||
| 363 | 435 | goto next_chunk; |
| 364 | 436 | } else if ($nextChunk === "\x68\x49\x53\x54") { // hIST |
| 365 | 437 | $this->hIST = new \stdClass; |
| 366 | - kys('hIST'); | |
| 438 | + $this->hIST->name = 'hIST'; | |
| 439 | + $this->hIST->length = $length; | |
| 440 | + kys(['hIST', $length, bin2hex($data)]); | |
| 441 | + $this->hIST->crc = $crc; | |
| 442 | + $this->hIST->crc32 = unpack('N', $crc)[1]; | |
| 443 | + $this->hIST->crcHex = bin2hex($crc); | |
| 367 | 444 | $png->imageHistogram = $this->hIST; |
| 445 | + kys($png); | |
| 368 | 446 | goto next_chunk; |
| 369 | 447 | } else if ($nextChunk === "\x70\x48\x59\x73") { // pHYs |
| 370 | 448 | $this->pHYs = new \stdClass; |
| 449 | + $this->pHYs->name = 'pHYs'; | |
| 450 | + $this->pHYs->length = $length; | |
| 371 | 451 | $this->pHYs->x = unpack('N', substr($data, 0, 4))[1]; |
| 372 | 452 | $this->pHYs->y = unpack('N', substr($data, 4, 4))[1]; |
| 373 | 453 | $unit = [ |
| @@ -383,11 +463,19 @@ | ||
| 383 | 463 | goto next_chunk; |
| 384 | 464 | } else if ($nextChunk === "\x73\x50\x4c\x54") { // sPLT |
| 385 | 465 | $this->sPLT = new \stdClass; |
| 386 | - kys('sPLT'); | |
| 466 | + $this->sPLT->name = 'sPLT'; | |
| 467 | + $this->sPLT->length = $length; | |
| 468 | + kys(['sPLT', $length, bin2hex($data)]); | |
| 469 | + $this->sPLT->crc = $crc; | |
| 470 | + $this->sPLT->crc32 = unpack('N', $crc)[1]; | |
| 471 | + $this->sPLT->crcHex = bin2hex($crc); | |
| 387 | 472 | $png->suggestedPalette = $this->sPLT; |
| 473 | + kys($png); | |
| 388 | 474 | goto next_chunk; |
| 389 | 475 | } else if ($nextChunk === "\x65\x58\x49\x66") { // eXIf |
| 390 | 476 | $this->eXIf = new \stdClass; |
| 477 | + $this->eXIf->name = 'eXIf'; | |
| 478 | + $this->eXIf->length = $length; | |
| 391 | 479 | |
| 392 | 480 | if (strlen($data) < 8) { |
| 393 | 481 | $this->eXIf->valid = false; |
| @@ -423,11 +511,19 @@ | ||
| 423 | 511 | goto next_chunk; |
| 424 | 512 | } else if ($nextChunk === "\x74\x49\x4d\x45") { // tIME |
| 425 | 513 | $this->tIME = new \stdClass; |
| 426 | - kys('tIME'); | |
| 514 | + $this->tIME->name = 'tIME'; | |
| 515 | + $this->tIME->length = $length; | |
| 516 | + kys(['tIME', $length, bin2hex($data)]); | |
| 517 | + $this->tIME->crc = $crc; | |
| 518 | + $this->tIME->crc32 = unpack('N', $crc)[1]; | |
| 519 | + $this->tIME->crcHex = bin2hex($crc); | |
| 427 | 520 | $png->lastModificationTime = $this->tIME; |
| 521 | + kys($png); | |
| 428 | 522 | goto next_chunk; |
| 429 | 523 | } else if ($nextChunk === "\x61\x63\x54\x4c") { // acTL |
| 430 | 524 | $this->acTL = new \stdClass; |
| 525 | + $this->acTL->name = 'acTL'; | |
| 526 | + $this->acTL->length = $length; | |
| 431 | 527 | $this->acTL->numFrames = unpack('N', substr($data, 0, 4))[1]; |
| 432 | 528 | $this->acTL->numPlays = unpack('N', substr($data, 4, 4))[1]; |
| 433 | 529 | $this->acTL->crc = $crc; |
| @@ -439,6 +535,8 @@ | ||
| 439 | 535 | if ($this->currentFrame !== null) $this->frames[] = (object)$this->currentFrame; |
| 440 | 536 | |
| 441 | 537 | $this->currentFrame = [ |
| 538 | + 'name' => 'fcTL', | |
| 539 | + 'length' => $length, | |
| 442 | 540 | 'sequenceNumber' => unpack('N', substr($data, 0, 4))[1], |
| 443 | 541 | 'width' => unpack('N', substr($data, 4, 4))[1], |
| 444 | 542 | 'height' => unpack('N', substr($data, 8, 4))[1], |
| @@ -467,6 +565,8 @@ | ||
| 467 | 565 | } |
| 468 | 566 | |
| 469 | 567 | $compressedData = ($nextChunk === "\x66\x64\x41\x54") ? substr($data, 4) : $data; |
| 568 | + $this->currentFrame['name'] = 'fdAT'; | |
| 569 | + $this->currentFrame['length'] = $length; | |
| 470 | 570 | $this->currentFrame['data'] = $compressedData; |
| 471 | 571 | $this->currentFrame['dataBase64'] = base64_encode($compressedData); |
| 472 | 572 | $this->currentFrame['dataHex'] = bin2hex($compressedData); |
| @@ -477,6 +577,7 @@ | ||
| 477 | 577 | $this->currentFrame['crcHex'] = bin2hex($crc); |
| 478 | 578 | |
| 479 | 579 | if ($nextChunk === "\x49\x44\x41\x54") { |
| 580 | + $this->currentFrame['name'] = 'IDAT'; | |
| 480 | 581 | $this->frames[] = (object)$this->currentFrame; |
| 481 | 582 | $this->currentFrame = null; |
| 482 | 583 | } |