3

There are a lot of solutions about how to create an APNG image (animated PNG), but how can I split APNG image frames into separate images?

Thanks in advance.

Timur Gafforov
  • 695
  • 1
  • 10
  • 25

2 Answers2

1

Here's some example code that will take a png in the form of a byte array and return the various frames as an array of byte arrays.

function splitapng($data) {
  $parts = array();

  // Save the PNG signature   
  $signature = substr($data, 0, 8);
  $offset = 8;
  $size = strlen($data);
  while ($offset < $size) {
    // Read the chunk length
    $length = substr($data, $offset, 4);
    $offset += 4;

    // Read the chunk type
    $type = substr($data, $offset, 4);
    $offset += 4;

    // Unpack the length and read the chunk data including 4 byte CRC
    $ilength = unpack('Nlength', $length);
    $ilength = $ilength['length'];
    $chunk = substr($data, $offset, $ilength+4); 
    $offset += $ilength+4;

    if ($type == 'IHDR')
      $header = $length . $type . $chunk;  // save the header chunk
    else if ($type == 'IEND')
      $end = $length . $type . $chunk;     // save the end chunk
    else if ($type == 'IDAT') 
      $parts[] = $length . $type . $chunk; // save the first frame
    else if ($type == 'fdAT') {
      // Animation frames need a bit of tweaking.
      // We need to drop the first 4 bytes and set the correct type.
      $length = pack('N', $ilength-4);
      $type = 'IDAT';
      $chunk = substr($chunk,4);
      $parts[] = $length . $type . $chunk;
    }
  }

  // Now we just add the signature, header, and end chunks to every part.
  for ($i = 0; $i < count($parts); $i++) {
    $parts[$i] = $signature . $header . $parts[$i] . $end;
  }

  return $parts;
}

An example call, loading a file and saving the parts:

$filename = 'example.png';

$handle = fopen($filename, 'rb');
$filesize = filesize($filename);
$data = fread($handle, $filesize);
fclose($handle);

$parts = splitapng($data);

for ($i = 0; $i < count($parts); $i++) {
  $handle = fopen("part-$i.png",'wb');
  fwrite($handle,$parts[$i]);
  fclose($handle);
}
James Holderness
  • 21,596
  • 2
  • 35
  • 49
  • 1
    Hi! I just noticed an error in the result of the script. For some reason only first frame is valid others have some error in the image. Because of that the images with the error are not valid in PHP and Firefox browser. Do you have any idea why? – Timur Gafforov Sep 11 '13 at 09:29
1

There is a small bug in splitapng() (wrong crc32) which produce corrupted png images ... fixed here: https://stackoverflow.com/a/61241937/13317744