Mercurial > hg > orthanc-imagej
view com/hjg/pngj/PngReaderApng.java @ 4:3f418d4451d6 pngj
add pngj
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 15 Apr 2016 09:52:58 +0200 |
parents | |
children |
line wrap: on
line source
package ar.com.hjg.pngj; import java.io.File; import java.io.InputStream; import java.util.List; import ar.com.hjg.pngj.chunks.PngChunk; import ar.com.hjg.pngj.chunks.PngChunkACTL; import ar.com.hjg.pngj.chunks.PngChunkFCTL; import ar.com.hjg.pngj.chunks.PngChunkFDAT; import ar.com.hjg.pngj.chunks.PngChunkIDAT; /** */ public class PngReaderApng extends PngReaderByte { public PngReaderApng(File file) { super(file); dontSkipChunk(PngChunkFCTL.ID); } public PngReaderApng(InputStream inputStream) { super(inputStream); dontSkipChunk(PngChunkFCTL.ID); } private Boolean apngKind = null; private boolean firsIdatApngFrame = false; protected PngChunkACTL actlChunk; // null if not APNG private PngChunkFCTL fctlChunk; // current (null for the pseudo still frame) /** * Current frame number (reading or read). First animated frame is 0. Frame -1 represents the IDAT (default image) * when it's not part of the animation */ protected int frameNum = -1; // incremented after each fctl finding public boolean isApng() { if (apngKind == null) { // this triggers the loading of first chunks; actlChunk = (PngChunkACTL) getChunksList().getById1(PngChunkACTL.ID); // null if not apng apngKind = actlChunk != null; firsIdatApngFrame = fctlChunk != null; } return apngKind.booleanValue(); } public void advanceToFrame(int frame) { if (frame < frameNum) throw new PngjInputException("Cannot go backwards"); if (frame >= getApngNumFrames()) throw new PngjInputException("Frame out of range " + frame); if (frame > frameNum) { addChunkToSkip(PngChunkIDAT.ID); addChunkToSkip(PngChunkFDAT.ID); while (frameNum < frame & !chunkseq.isDone()) if (streamFeeder.feed(chunkseq) <= 0) break; } if (frame == frameNum) { // prepare to read rows. at this point we have a new dontSkipChunk(PngChunkIDAT.ID); dontSkipChunk(PngChunkFDAT.ID); rowNum = -1; imlinesSet = null;// force recreation (this is slightly dirty) // seek the next IDAT/fDAT - TODO: set the expected sequence number while (!chunkseq.isDone() && !chunkseq.getCurChunkReader().isFromDeflatedSet()) if (streamFeeder.feed(chunkseq) <= 0) break; } else { throw new PngjInputException("unexpected error seeking from frame " + frame); } } /** * True if it has a default image (IDAT) that is not part of the animation. In that case, we consider it as a * pseudo-frame (number -1) */ public boolean hasExtraStillImage() { return isApng() && !firsIdatApngFrame; } /** * Only counts true animation frames. */ public int getApngNumFrames() { if (isApng()) return actlChunk.getNumFrames(); else return 0; } /** * 0 if it's to been played infinitely. -1 if not APNG */ public int getApngNumPlays() { if (isApng()) return actlChunk.getNumPlays(); else return -1; } @Override public IImageLine readRow() { // TODO Auto-generated method stub return super.readRow(); } @Override public boolean hasMoreRows() { // TODO Auto-generated method stub return super.hasMoreRows(); } @Override public IImageLine readRow(int nrow) { // TODO Auto-generated method stub return super.readRow(nrow); } @Override public IImageLineSet<? extends IImageLine> readRows() { // TODO Auto-generated method stub return super.readRows(); } @Override public IImageLineSet<? extends IImageLine> readRows(int nRows, int rowOffset, int rowStep) { // TODO Auto-generated method stub return super.readRows(nRows, rowOffset, rowStep); } @Override public void readSkippingAllRows() { // TODO Auto-generated method stub super.readSkippingAllRows(); } @Override protected ChunkSeqReaderPng createChunkSeqReader() { ChunkSeqReaderPng cr = new ChunkSeqReaderPng(false) { @Override public boolean shouldSkipContent(int len, String id) { return super.shouldSkipContent(len, id); } @Override protected boolean isIdatKind(String id) { return id.equals(PngChunkIDAT.ID) || id.equals(PngChunkFDAT.ID); } @Override protected DeflatedChunksSet createIdatSet(String id) { IdatSet ids = new IdatSet(id, getCurImgInfo(), deinterlacer); ids.setCallbackMode(callbackMode); return ids; } @Override protected void startNewChunk(int len, String id, long offset) { super.startNewChunk(len, id, offset); } @Override protected void postProcessChunk(ChunkReader chunkR) { super.postProcessChunk(chunkR); if (chunkR.getChunkRaw().id.equals(PngChunkFCTL.ID)) { frameNum++; List<PngChunk> chunkslist = chunkseq.getChunks(); fctlChunk = (PngChunkFCTL) chunkslist.get(chunkslist.size() - 1); // as this is slightly dirty, we check if (chunkR.getChunkRaw().getOffset() != fctlChunk.getRaw().getOffset()) throw new PngjInputException("something went wrong"); ImageInfo frameInfo = fctlChunk.getEquivImageInfo(); getChunkseq().updateCurImgInfo(frameInfo); } } @Override protected boolean countChunkTypeAsAncillary(String id) { // we don't count fdat as ancillary data return super.countChunkTypeAsAncillary(id) && !id.equals(id.equals(PngChunkFDAT.ID)); } }; return cr; } /** * @see #frameNum */ public int getFrameNum() { return frameNum; } @Override public void end() { // TODO Auto-generated method stub super.end(); } public PngChunkFCTL getFctl() { return fctlChunk; } }