1

APNGファイルからすべてのpngファイルを抽出しようとしています。私は助けを探しましたが、あまりありません。私が見つけることができるのは、オープン ソース ライブラリ pngj だけであり、それを使用して APNG ファイルの最初のフレームを取得できます。

これが私が使用しているコードです

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ファイルとして保存する方法を教えてもらえますか?どんなヘルプやヒントも適用されます

4

2 に答える 2

4

実際、PNGJライブラリ(私は作者です)はAPNG標準をサポートしていません(余談:APGNアプローチが好きではなく、ライブラリのアイデアに適合しなかったため、これに反対しました:「巨大な」データのロード-IDAT- プログレッシブ、ラインごと; チャンク (メタデータ) をメモリに直接ロード; APGN は、フレームをチャンクに保存することで PGN 標準を悪用します)。

とにかくそれを使ってやりたいことをやろうとすることはできますが、エレガントで堅牢な方法ではありません. これが例です。醜いだけでなく、これは部分的なフレーム (APNG がサポートするフル画像よりも小さいフレーム)、オーバーレイ、パレット画像 (後の問題が最も修正しやすいでしょう) では機能しません(うまくいけば修正されます)。

これはhttp://philip.html5.org/tests/apng/028.pngでテストされました

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"));
    }

}
于 2012-10-12T17:29:18.410 に答える
1

役立つリソース:

  1. これらの例は apng ファイルを読み取る方法を示していますが、これは C であり、libpng を使用しています: https://sourceforge.net/projects/apng/files/libpng/examples/

  2. ここにいくつかの Java コードがありますが、APNG ファイルを作成することしかできず、読み取ることはできません: https://www.reto-hoehener.ch/japng/index.html

  3. APNG ファイルを読み込んで表示するための JavaScript コードは次のとおりです: https://github.com/davidmz/apng-canvas

于 2012-10-12T15:59:10.853 に答える