将PNG图像加入到APNG动画图像中

时间:2013-08-18 09:22:49

标签: javascript node.js apng

是否有可能以某种方式使用nodejs将PNG图像连接到APNG动画图像?

我只找到了PHP库:link

4 个答案:

答案 0 :(得分:3)

UPNG.js 可以解析和构建APNG文件 - https://github.com/photopea/UPNG.js

从自述文件 -

  

UPNG.js支持APNG,界面需要"帧"。

     

UPNG.encode(imgs,w,h,cnum,[dels])

imgs: array of frames. A frame is an ArrayBuffer containing the pixel 
      data (RGBA, 8 bits per channel)
w, h : width and height of the image
cnum: number of colors in the result; 0: all colors (lossless PNG)
dels: array of delays for each frame (only when 2 or more frames)
returns an ArrayBuffer with binary data of a PNG file
     

UPNG.js可以对PNG文件进行有损的缩小,类似于TinyPNG   和其他工具。它使用k均值执行颜色量化   算法

     

最后一个参数cnum允许有损压缩。设置为   无损压缩为零,或写入允许的颜色数   在图像中。较小的值会产生较小的文件。或者只使用0表示   无损/ 256有损。

答案 1 :(得分:2)

我不确定nodejs,但您可以尝试APNG-canvas。 APNG使用HTML5(-webkit-canvas),JavaScript(jQuery)。

“APNG-canvas是一个库,用于在支持画布的浏览器中替换动画PNG文件(谷歌浏览器,Internet Explorer 9,Apple Safari)。”

工作demo就在这里。

答案 2 :(得分:2)

Currently,不,它看起来不像。 Wikipedia lists the available software,正如您所看到的,不支持具有Node包装器的ImageMagick。但是,您可能会发现可以下载command line tool apngasm并向其发送shell,如果您发现它值得您使用child_process({{3}将节点命令行包装器挂钩到现有应用程序中。 }})。

答案 3 :(得分:2)

没有可用的库,但实现起来非常简单。 Wikipedia

中描述了将多个PNG文件合并为单个APNG的算法
  
      
  1. 将第一个PNG文件的所有块作为构建基础。
  2.   
  3. 在图像标题块(IHDR)之后插入动画控制块(acTL)。
  4.   
  5. 如果要将第一个PNG作为动画的一部分,请在图像数据块(IDAT)之前插入一个帧控制块(fcTL)。
  6.   
  7. 对于每个剩余帧,添加帧控制块(fcTL)和帧数据块(fdAT)。然后添加图像结束块(IEND)。帧数据块(fdAT)的内容取自其各自源图像的图像数据块(IDAT)。
  8.   

以下是一个示例实现:

const fs = require('fs')
const crc32 = require('crc').crc32

function findChunk(buffer, type) {
  let offset = 8

  while (offset < buffer.length) {
    let chunkLength = buffer.readUInt32BE(offset)
    let chunkType = buffer.slice(offset + 4, offset + 8).toString('ascii')

    if (chunkType === type) {
      return buffer.slice(offset, offset + chunkLength + 12)
    }

    offset += 4 + 4 + chunkLength + 4
  }

  throw new Error(`Chunk "${type}" not found`)
}

const images = process.argv.slice(2).map(path => fs.readFileSync(path))

const actl = Buffer.alloc(20)
actl.writeUInt32BE(8, 0)                                    // length of chunk
actl.write('acTL', 4)                                       // type of chunk
actl.writeUInt32BE(images.length, 8)                        // number of frames
actl.writeUInt32BE(0, 12)                                   // number of times to loop (0 - infinite)
actl.writeUInt32BE(crc32(actl.slice(4, 16)), 16)            // crc

const frames = images.map((data, idx) => {
  const ihdr = findChunk(data, 'IHDR')

  const fctl = Buffer.alloc(38)
  fctl.writeUInt32BE(26, 0)                                 // length of chunk
  fctl.write('fcTL', 4)                                     // type of chunk
  fctl.writeUInt32BE(idx ? idx * 2 - 1 : 0, 8)              // sequence number
  fctl.writeUInt32BE(ihdr.readUInt32BE(8), 12)              // width
  fctl.writeUInt32BE(ihdr.readUInt32BE(12), 16)             // height
  fctl.writeUInt32BE(0, 20)                                 // x offset
  fctl.writeUInt32BE(0, 24)                                 // y offset
  fctl.writeUInt16BE(1, 28)                                 // frame delay - fraction numerator
  fctl.writeUInt16BE(1, 30)                                 // frame delay - fraction denominator
  fctl.writeUInt8(0, 32)                                    // dispose mode
  fctl.writeUInt8(0, 33)                                    // blend mode
  fctl.writeUInt32BE(crc32(fctl.slice(4, 34)), 34)          // crc

  const idat = findChunk(data, 'IDAT')

  // All IDAT chunks except first one are converted to fdAT chunks
  let fdat;

  if (idx === 0) {
    fdat = idat
  } else {
    const length = idat.length + 4

    fdat = Buffer.alloc(length)

    fdat.writeUInt32BE(length - 12, 0)                      // length of chunk
    fdat.write('fdAT', 4)                                   // type of chunk
    fdat.writeUInt32BE(idx * 2, 8)                          // sequence number
    idat.copy(fdat, 12, 8)                                  // image data
    fdat.writeUInt32BE(crc32(4, length - 4), length - 4)    // crc
  }

  return Buffer.concat([ fctl, fdat ])
})

const signature = Buffer.from('\211PNG\r\n\032\n', 'ascii')
const ihdr = findChunk(images[0], 'IHDR')
const iend = Buffer.from('0000000049454e44ae426082', 'hex')

const output = Buffer.concat([ signature, ihdr, actl, ...frames, iend ])

fs.writeFileSync('output.png', output)