是否有可能以某种方式使用nodejs将PNG图像连接到APNG动画图像?
我只找到了PHP库:link
答案 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的算法
- 将第一个PNG文件的所有块作为构建基础。
- 在图像标题块(IHDR)之后插入动画控制块(acTL)。
- 如果要将第一个PNG作为动画的一部分,请在图像数据块(IDAT)之前插入一个帧控制块(fcTL)。
- 对于每个剩余帧,添加帧控制块(fcTL)和帧数据块(fdAT)。然后添加图像结束块(IEND)。帧数据块(fdAT)的内容取自其各自源图像的图像数据块(IDAT)。
醇>
以下是一个示例实现:
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)