All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ar.com.hjg.pngj.ChunkSeqReaderPng Maven / Gradle / Ivy

package ar.com.hjg.pngj;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import ar.com.hjg.pngj.ChunkReader.ChunkReaderMode;
import ar.com.hjg.pngj.chunks.ChunkFactory;
import ar.com.hjg.pngj.chunks.ChunkHelper;
import ar.com.hjg.pngj.chunks.ChunkLoadBehaviour;
import ar.com.hjg.pngj.chunks.ChunksList;
import ar.com.hjg.pngj.chunks.PngChunk;
import ar.com.hjg.pngj.chunks.PngChunkIDAT;
import ar.com.hjg.pngj.chunks.PngChunkIEND;
import ar.com.hjg.pngj.chunks.PngChunkIHDR;
import ar.com.hjg.pngj.chunks.PngChunkPLTE;

/**
 * Adds to ChunkSeqReader the storing of PngChunk, with a PngFactory, and
 * imageInfo + deinterlacer.
 * 

* Most usual PNG reading should use this class, or a {@link PngReader}, which * is a thin wrapper over this. */ public class ChunkSeqReaderPng extends ChunkSeqReader { protected ImageInfo imageInfo; // initialized at parsing the IHDR protected Deinterlacer deinterlacer; protected int currentChunkGroup = -1; /** * All chunks, but some of them can have the buffer empty (IDAT and skipped) */ protected ChunksList chunksList = null; protected final boolean callbackMode; private long bytesChunksLoaded = 0; // bytes loaded from buffered chunks non-critical chunks (data only) private boolean checkCrc = true; // --- parameters to be set prior to reading --- private boolean includeNonBufferedChunks = false; private Set chunksToSkip = new HashSet(); private long maxTotalBytesRead = 0; private long skipChunkMaxSize = 0; private long maxBytesMetadata = 0; private IChunkFactory chunkFactory; private ChunkLoadBehaviour chunkLoadBehaviour = ChunkLoadBehaviour.LOAD_CHUNK_ALWAYS; public ChunkSeqReaderPng(boolean callbackMode) { super(); this.callbackMode = callbackMode; chunkFactory = new ChunkFactory(); // default factory } private void updateAndCheckChunkGroup(String id) { if (id.equals(PngChunkIHDR.ID)) { // IDHR if (currentChunkGroup < 0) currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR; else throw new PngjInputException("unexpected chunk " + id); } else if (id.equals(PngChunkPLTE.ID)) { // PLTE if ((currentChunkGroup == ChunksList.CHUNK_GROUP_0_IDHR || currentChunkGroup == ChunksList.CHUNK_GROUP_1_AFTERIDHR)) currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE; else throw new PngjInputException("unexpected chunk " + id); } else if (id.equals(PngChunkIDAT.ID)) { // IDAT (no necessarily the first) if ((currentChunkGroup >= ChunksList.CHUNK_GROUP_0_IDHR && currentChunkGroup <= ChunksList.CHUNK_GROUP_4_IDAT)) currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT; else throw new PngjInputException("unexpected chunk " + id); } else if (id.equals(PngChunkIEND.ID)) { // END if ((currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT)) currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; else throw new PngjInputException("unexpected chunk " + id); } else { // ancillary if (currentChunkGroup <= ChunksList.CHUNK_GROUP_1_AFTERIDHR) currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; else if (currentChunkGroup <= ChunksList.CHUNK_GROUP_3_AFTERPLTE) currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; else currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; } } @Override public boolean shouldSkipContent(int len, String id) { if (super.shouldSkipContent(len, id)) return true; if (ChunkHelper.isCritical(id)) return false;// critical chunks are never skipped if (maxTotalBytesRead > 0 && len + getBytesCount() > maxTotalBytesRead) throw new PngjInputException("Maximum total bytes to read exceeeded: " + maxTotalBytesRead + " offset:" + getBytesCount() + " len=" + len); if (chunksToSkip.contains(id)) return true; // specific skip if (skipChunkMaxSize > 0 && len > skipChunkMaxSize) return true; // too big chunk if (maxBytesMetadata > 0 && len > maxBytesMetadata - bytesChunksLoaded) return true; // too much ancillary chunks loaded switch (chunkLoadBehaviour) { case LOAD_CHUNK_IF_SAFE: if (!ChunkHelper.isSafeToCopy(id)) return true; break; case LOAD_CHUNK_NEVER: return true; default: break; } return false; } public long getBytesChunksLoaded() { return bytesChunksLoaded; } public int getCurrentChunkGroup() { return currentChunkGroup; } public void setChunksToSkip(String... chunksToSkip) { this.chunksToSkip.clear(); for (String c : chunksToSkip) this.chunksToSkip.add(c); } public void addChunkToSkip(String chunkToSkip) { this.chunksToSkip.add(chunkToSkip); } public boolean firstChunksNotYetRead() { return getCurrentChunkGroup() < ChunksList.CHUNK_GROUP_4_IDAT; } @Override protected void postProcessChunk(ChunkReader chunkR) { super.postProcessChunk(chunkR); if (chunkR.getChunkRaw().id.equals(PngChunkIHDR.ID)) { PngChunkIHDR ch = new PngChunkIHDR(null); ch.parseFromRaw(chunkR.getChunkRaw()); imageInfo = ch.createImageInfo(); if (ch.isInterlaced()) deinterlacer = new Deinterlacer(imageInfo); chunksList = new ChunksList(imageInfo); } if (chunkR.mode == ChunkReaderMode.BUFFER || includeNonBufferedChunks) { PngChunk chunk = chunkFactory.createChunk(chunkR.getChunkRaw(), getImageInfo()); chunksList.appendReadChunk(chunk, currentChunkGroup); } if (isDone()) { processEndPng(); } } /** check that the last inserted chunk had the correct ordering */ /*protected void checkOrdering() { PngChunk c = chunksList.getChunks().get(chunksList.getChunks().size() - 1); ChunkOrderingConstraint oc = c.getOrderingConstraint(); //chunksList.getById1(); PngHelperInternal.LOGGER.warning("check ordering not implemented"); }*/ @Override protected DeflatedChunksSet createIdatSet(String id) { IdatSet ids = new IdatSet(id, imageInfo, deinterlacer); ids.setCallbackMode(callbackMode); return ids; } public IdatSet getIdatSet() { DeflatedChunksSet c = getCurReaderDeflatedSet(); return c instanceof IdatSet ? (IdatSet) c : null; } @Override protected boolean isIdatKind(String id) { return id.equals(PngChunkIDAT.ID); } @Override public int consume(byte[] buf, int off, int len) { return super.consume(buf, off, len); } /** * sets a custom chunk factory. This is typically called with a custom class * extends ChunkFactory, to adds custom chunks to the default well-know ones * * @param chunkFactory */ public void setChunkFactory(IChunkFactory chunkFactory) { this.chunkFactory = chunkFactory; } /** * Things to be done after IEND processing. This is not called if * prematurely closed. */ protected void processEndPng() { // nothing to do } public ImageInfo getImageInfo() { return imageInfo; } public boolean isInterlaced() { return deinterlacer != null; } public Deinterlacer getDeinterlacer() { return deinterlacer; } @Override protected void startNewChunk(int len, String id, long offset) { updateAndCheckChunkGroup(id); super.startNewChunk(len, id, offset); } @Override public void close() { if (currentChunkGroup != ChunksList.CHUNK_GROUP_6_END)// this could only happen if forced close currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; super.close(); } public List getChunks() { return chunksList.getChunks(); } public void setMaxTotalBytesRead(long maxTotalBytesRead) { this.maxTotalBytesRead = maxTotalBytesRead; } public long getSkipChunkMaxSize() { return skipChunkMaxSize; } public void setSkipChunkMaxSize(long skipChunkMaxSize) { this.skipChunkMaxSize = skipChunkMaxSize; } public long getMaxBytesMetadata() { return maxBytesMetadata; } public void setMaxBytesMetadata(long maxBytesMetadata) { this.maxBytesMetadata = maxBytesMetadata; } public long getMaxTotalBytesRead() { return maxTotalBytesRead; } @Override protected boolean shouldCheckCrc(int len, String id) { return checkCrc; } public boolean isCheckCrc() { return checkCrc; } public void setCheckCrc(boolean checkCrc) { this.checkCrc = checkCrc; } public boolean isCallbackMode() { return callbackMode; } public Set getChunksToSkip() { return chunksToSkip; } public void setChunkLoadBehaviour(ChunkLoadBehaviour chunkLoadBehaviour) { this.chunkLoadBehaviour = chunkLoadBehaviour; } /** * If true, the chunks with no data (because skipped or because processed * like IDAT-type) are still stored in the PngChunks list, which might be * more informative. * * Setting this to false saves a few bytes * * Default: false * * @param includeNonBufferedChunks */ public void setIncludeNonBufferedChunks(boolean includeNonBufferedChunks) { this.includeNonBufferedChunks = includeNonBufferedChunks; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy