在java中从APNG中提取png文件
我一直在尝试从APNG文件中提取所有png文件。 我已经找人帮忙了,但是没有太多。 我所能找到的就是一个开源库pngj,有了它,我就可以得到APNG文件的第一帧 这是我正在使用的代码在java中从APNG中提取png文件,java,png,apng,Java,Png,Apng,我一直在尝试从APNG文件中提取所有png文件。 我已经找人帮忙了,但是没有太多。 我所能找到的就是一个开源库pngj,有了它,我就可以得到APNG文件的第一帧 这是我正在使用的代码 public static void mirror(File orig, File dest, boolean overwrite) { PngReader pngr = FileHelper.createPngReader(orig); PngWriter pngw = File
public static void mirror(File orig, File dest, boolean overwrite)
{
PngReader pngr = FileHelper.createPngReader(orig);
PngWriter pngw = FileHelper.createPngWriter(dest, pngr.imgInfo,
overwrite);
pngw.setFilterType(FilterType.FILTER_CYCLIC); // just to test all
// filters
int copyPolicy = ChunkCopyBehaviour.COPY_ALL;
pngw.copyChunksFirst(pngr, copyPolicy);
ImageLine lout = new ImageLine(pngw.imgInfo);
int cols = pngr.imgInfo.cols;
int channels = pngr.imgInfo.channels;
int[] line = new int[cols * channels];
int aux;
for (int row = 0; row < pngr.imgInfo.rows; row++) {
ImageLine l1 = pngr.readRow(row);
line = l1.unpack(line, false);
for (int c1 = 0, c2 = cols - 1; c1 < c2; c1++, c2--) {
for (int i = 0; i < channels; i++) {
aux = line[c1 * channels + i];
line[c1 * channels + i] = line[c2 * channels + i];
line[c2 * channels + i] = aux;
}
}
lout.pack(line, false);
pngw.writeRow(lout, row);
}
pngr.end();
pngw.copyChunksLast(pngr, copyPolicy);
pngw.end();
// // print unknown chunks, just for information
List<PngChunk> u = ChunkHelper.filterList(pngr.getChunksList()
.getChunks(), new ChunkPredicate() {
public boolean match(PngChunk c) {
return ChunkHelper.isUnknown(c);
}
});
if (!u.isEmpty())
System.out.println("Unknown chunks:" + u);
}
所以基本上我只是镜像一个apng文件,它被转换成一个png文件,这是第一帧。
那么,有人能告诉我如何获取剩余的帧并将其保存为png文件吗?
任何帮助或提示都将被告知一些可能有帮助的资源: 这些示例显示了如何读取apng文件,但它是C语言,并且使用libpng: 下面是一些Java代码,但它只能创建APNG文件,不能读取: 以下是用于读取和显示APNG文件的JavaScript代码:
一些可能有帮助的资源: 这些示例显示了如何读取apng文件,但它是C语言,并且使用libpng: 下面是一些Java代码,但它只能创建APNG文件,不能读取: 以下是用于读取和显示APNG文件的JavaScript代码: 事实上,我是作者的图书馆不支持APNG标准的离题:我决定反对它,因为我不喜欢APGN方法,因为它不符合我图书馆的理念:逐行渐进地加载大量数据(IDAT);直接将块元数据加载到内存中;APGN滥用PGN标准,将帧存储在块中 你可以尝试用它来做任何你想做的事情,但不是用一种优雅而健壮的方式。这里有一个例子。除了难看之外,这不适用于APNG支持的小于完整图像的部分帧,也不适用于覆盖,也不适用于苍白图像,希望后一个问题最容易修复 这一点已通过测试 事实上,我是作者的图书馆不支持APNG标准的离题:我决定反对它,因为我不喜欢APGN方法,因为它不符合我图书馆的理念:逐行渐进地加载大量数据(IDAT);直接将块元数据加载到内存中;APGN滥用PGN标准,将帧存储在块中 你可以尝试用它来做任何你想做的事情,但不是用一种优雅而健壮的方式。这里有一个例子。除了难看之外,这不适用于APNG支持的小于完整图像的部分帧,也不适用于覆盖,也不适用于苍白图像,希望后一个问题最容易修复 这一点已通过测试
好的,非常感谢代码,我自己从来都不赞成apng文件,因为它的大小,但它有点难让您的员工了解:P好的,有了这个代码,我可以处理任何小于50-60帧的apng,它的工作原理就像一个champ,但是如果我超出这个范围,图像似乎被破坏了,信息丢失或完全消失了,当我试图看到图像有什么问题时,它会说丢失的图像数据或无效的压缩数据。知道为什么会这样吗?我需要看看有问题的图片。您也可以使用nice工具查看图像内部,查看块结构中的某些特定模式是否出现问题,例如,每帧有多个fdat块,等等。您可以看到,所有无法提取的apng图像每帧都有多个fdat块,那么,我是否应该尝试将所有fdat块放在一个帧中,并使它们成为一个?抱歉,如果我听起来像个十足的傻瓜,因为事实上,当涉及到处理imagesCode fixed时,请尝试一下。我误解了fdat序列号的含义。不客气。我再次更新了代码,稍微不那么难看了,现在应该可以使用调色板了,但我没有测试好。多亏了代码,我自己从来都不喜欢apng文件,因为文件太大了,但要让你的员工理解这一点有点困难:P好吧,有了这一代码,我可以处理任何小于50-60帧的apng文件,它的工作原理就像一个champ,但是如果我超出这个范围,图像似乎被破坏了,信息丢失或完全消失了,当我试图看到图像有什么问题时,它会说丢失的图像数据或无效的压缩数据。知道为什么会这样吗?我需要看看有问题的图片。您也可以使用nice工具查看图像内部,查看块结构中的某些特定模式是否出现问题,例如,每帧有多个fdat块,等等。您可以看到,所有无法提取的apng图像每帧都有多个fdat块,那么,我是否应该尝试将所有fdat块放在一个帧中,并使它们成为一个?对不起,如果我听起来像个十足的傻瓜,因为实际上我是
在处理图像方面,代码已修复,请尝试。我误解了fdat序列号的含义。不客气。我再次更新了代码,稍微不那么难看,现在应该可以使用调色板了,但我还没有测试过
import java.io.File;
import java.io.FileOutputStream;
import ar.com.hjg.pngj.FileHelper;
import ar.com.hjg.pngj.ImageLine;
import ar.com.hjg.pngj.PngHelperInternal;
import ar.com.hjg.pngj.PngReader;
import ar.com.hjg.pngj.PngWriter;
import ar.com.hjg.pngj.chunks.*;
public class ApngSplit {
private static final String PREFIX = "apngf";
/** reads a APNG file and tries to split it into its frames */
public static void process(File orig) throws Exception {
PngReader pngr = FileHelper.createPngReader(orig);
File dest = new File(orig.getParent(), PREFIX + "0_" + orig.getName());
PngWriter pngw = FileHelper.createPngWriter(dest, pngr.imgInfo, true);
System.out.println("writing default frame " + pngw.getFilename());
pngr.setChunkLoadBehaviour(ChunkLoadBehaviour.LOAD_CHUNK_ALWAYS);
pngr.setMaxBytesMetadata(Integer.MAX_VALUE);
pngr.setMaxTotalBytesRead(Long.MAX_VALUE);
pngr.setSkipChunkIds(new String[] {});
int copyPolicy = ChunkCopyBehaviour.COPY_PALETTE | ChunkCopyBehaviour.COPY_ALL_SAFE;
pngw.copyChunksFirst(pngr, copyPolicy);
int cols = pngr.imgInfo.cols;
int channels = pngr.imgInfo.channels;
for (int row = 0; row < pngr.imgInfo.rows; row++) {
ImageLine l1 = pngr.readRow(row);
pngw.writeRow(l1, row);
}
pngr.end();
pngw.copyChunksLast(pngr, copyPolicy);
pngw.end();
processExtra2(orig, pngr.getChunksList());
}
private static void processExtra2(File orig, ChunksList chunks) throws Exception {
int numframe = 0;
FileOutputStream os = null;
boolean afterIdat = false;
for (PngChunk chunkApng : chunks.getChunks()) {
if (chunkApng.id.equals("IDAT"))
afterIdat = true;
if (chunkApng.id.equals("fcTL") && afterIdat) {
numframe++;
if (os != null)
endPng(chunks, os);
File dest = new File(orig.getParent(), PREFIX + numframe + "_" + orig.getName());
System.out.println("writing seq " + numframe + " : " + dest);
os = new FileOutputStream(dest);
beginPng(chunks, os);
}
if (chunkApng.id.equals("fdAT")) {
ChunkRaw crawf = chunkApng.createRawChunk();
int seq = PngHelperInternal.readInt4fromBytes(crawf.data, 0);
ChunkRaw crawi = new ChunkRaw(crawf.len - 4, ChunkHelper.b_IDAT, true);
System.arraycopy(crawf.data, 4, crawi.data, 0, crawi.data.length);
crawi.writeChunk(os);
}
}
if (os != null)
endPng(chunks, os);
}
private static void endPng(ChunksList chunks, FileOutputStream fos) throws Exception {
chunks.getById1(PngChunkIEND.ID).createRawChunk().writeChunk(fos);
fos.close();
}
private static void beginPng(ChunksList chunks, FileOutputStream fos) throws Exception {
fos.write(new byte[] { -119, 80, 78, 71, 13, 10, 26, 10 }); // signature
chunks.getById1(PngChunkIHDR.ID).createRawChunk().writeChunk(fos);
PngChunk plte = chunks.getById1(PngChunkPLTE.ID);
if (plte != null)
plte.createRawChunk().writeChunk(fos);
}
public static void main(String[] args) throws Exception {
process(new File("C:/temp/029.png"));
}
}