package jogamp.opengl.util.pngj.chunks; import java.io.OutputStream; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; import jogamp.opengl.util.pngj.ImageInfo; import jogamp.opengl.util.pngj.PngjExceptionInternal; /** * Represents a instance of a PNG chunk. *
* See http://www * .libpng.org/pub/png/spec/1.2/PNG-Chunks .html *
* Concrete classes should extend {@link PngChunkSingle} or * {@link PngChunkMultiple} *
* Note that some methods/fields are type-specific (getOrderingConstraint(),
* allowsMultiple()),
* some are 'almost' type-specific (id,crit,pub,safe; the exception is
* PngUKNOWN),
* and the rest are instance-specific
*/
public abstract class PngChunk {
/**
* Chunk-id: 4 letters
*/
public final String id;
/**
* Autocomputed at creation time
*/
public final boolean crit, pub, safe;
protected final ImageInfo imgInfo;
/**
* Possible ordering constraint for a PngChunk type -only relevant for
* ancillary chunks. Theoretically, there could be more general constraints,
* but these cover the constraints for standard chunks.
*/
public enum ChunkOrderingConstraint {
/**
* no ordering constraint
*/
NONE,
/**
* Must go before PLTE (and hence, also before IDAT)
*/
BEFORE_PLTE_AND_IDAT,
/**
* Must go after PLTE but before IDAT
*/
AFTER_PLTE_BEFORE_IDAT,
/**
* Must before IDAT (before or after PLTE)
*/
BEFORE_IDAT,
/**
* Does not apply
*/
NA;
public boolean mustGoBeforePLTE() {
return this == BEFORE_PLTE_AND_IDAT;
}
public boolean mustGoBeforeIDAT() {
return this == BEFORE_IDAT || this == BEFORE_PLTE_AND_IDAT || this == AFTER_PLTE_BEFORE_IDAT;
}
public boolean mustGoAfterPLTE() {
return this == AFTER_PLTE_BEFORE_IDAT;
}
}
private boolean priority = false; // For writing. Queued chunks with high priority will be written as soon as
// possible
protected int chunkGroup = -1; // chunk group where it was read or writen
protected int length = -1; // merely informational, for read chunks
protected long offset = 0; // merely informational, for read chunks
/**
* This static map defines which PngChunk class correspond to which ChunkID
*
* The client can add other chunks to this map statically, before reading an
* image, calling PngChunk.factoryRegister(id,class)
*/
private final static Map
* This method should be called by user code that wants to add some chunks
* (not implmemented in this library) to the factory, so that the PngReader
* knows about it.
*/
public static void factoryRegister(final String chunkId, final Class extends PngChunk> chunkClass) {
factoryMap.put(chunkId, chunkClass);
}
/**
* True if the chunk-id type is known.
*
* A chunk is known if we recognize its class, according with
*
* This is not necessarily the same as being "STANDARD", or being
* implemented in this library
*
* Unknown chunks will be parsed as instances of {@link PngChunkUNKNOWN}
*/
public static boolean isKnown(final String id) {
return factoryMap.containsKey(id);
}
protected PngChunk(final String id, final ImageInfo imgInfo) {
this.id = id;
this.imgInfo = imgInfo;
this.crit = ChunkHelper.isCritical(id);
this.pub = ChunkHelper.isPublic(id);
this.safe = ChunkHelper.isSafeToCopy(id);
}
/**
* This factory creates the corresponding chunk and parses the raw chunk.
* This is used when reading.
*/
public static PngChunk factory(final ChunkRaw chunk, final ImageInfo info) {
final PngChunk c = factoryFromId(ChunkHelper.toString(chunk.idbytes), info);
c.length = chunk.len;
c.parseFromRaw(chunk);
return c;
}
/**
* Creates one new blank chunk of the corresponding type, according to
* factoryMap (PngChunkUNKNOWN if not known)
*/
public static PngChunk factoryFromId(final String cid, final ImageInfo info) {
PngChunk chunk = null;
try {
final Class extends PngChunk> cla = factoryMap.get(cid);
if (cla != null) {
final Constructor extends PngChunk> constr = cla.getConstructor(ImageInfo.class);
chunk = constr.newInstance(info);
}
} catch (final Exception e) {
// this can happen for unkown chunks
}
if (chunk == null)
chunk = new PngChunkUNKNOWN(cid, info);
return chunk;
}
protected final ChunkRaw createEmptyChunk(final int len, final boolean alloc) {
final ChunkRaw c = new ChunkRaw(len, ChunkHelper.toBytes(id), alloc);
return c;
}
/**
* Makes a clone (deep copy) calling {@link #cloneDataFromRead(PngChunk)}
*/
@SuppressWarnings("unchecked")
public static
* -1 if not read or written (eg, queued)
*/
final public int getChunkGroup() {
return chunkGroup;
}
/**
* @see #getChunkGroup()
*/
final public void setChunkGroup(final int chunkGroup) {
this.chunkGroup = chunkGroup;
}
public boolean hasPriority() {
return priority;
}
public void setPriority(final boolean priority) {
this.priority = priority;
}
final void write(final OutputStream os) {
final ChunkRaw c = createRawChunk();
if (c == null)
throw new PngjExceptionInternal("null chunk ! creation failed for " + this);
c.writeChunk(os);
}
public int getLength() {
return length;
}
/*
* public void setLength(int length) { this.length = length; }
*/
public long getOffset() {
return offset;
}
public void setOffset(final long offset) {
this.offset = offset;
}
/**
* Creates the physical chunk. This is used when writing (serialization).
* Each particular chunk class implements its own logic.
*
* @return A newly allocated and filled raw chunk
*/
public abstract ChunkRaw createRawChunk();
/**
* Parses raw chunk and fill inside data. This is used when reading
* (deserialization). Each particular chunk class implements its own logic.
*/
public abstract void parseFromRaw(ChunkRaw c);
/**
* Makes a copy of the chunk.
*
* This is used when copying chunks from a reader to a writer
*
* It should normally be a deep copy, and after the cloning
* this.equals(other) should return true
*/
public abstract void cloneDataFromRead(PngChunk other);
public abstract boolean allowsMultiple(); // this is implemented in PngChunkMultiple/PngChunSingle
/**
* see {@link ChunkOrderingConstraint}
*/
public abstract ChunkOrderingConstraint getOrderingConstraint();
@Override
public String toString() {
return "chunk id= " + id + " (len=" + length + " offset=" + offset + ") c=" + getClass().getSimpleName();
}
}
factoryMap
*