package jogamp.opengl.util.pngj.chunks; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.CRC32; import jogamp.opengl.util.pngj.PngHelperInternal; import jogamp.opengl.util.pngj.PngjBadCrcException; import jogamp.opengl.util.pngj.PngjOutputException; /** * Raw (physical) chunk. *
* Short lived object, to be created while serialing/deserializing Do not reuse
* it for different chunks.
* See http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
*/
public class ChunkRaw {
/**
* The length counts only the data field, not itself, the chunk type code,
* or the CRC. Zero is a valid length. Although encoders and decoders should
* treat the length as unsigned, its value must not exceed 231-1 bytes.
*/
public final int len;
/**
* A 4-byte chunk type code. uppercase and lowercase ASCII letters
*/
public final byte[] idbytes = new byte[4];
/**
* The data bytes appropriate to the chunk type, if any. This field can be
* of zero length. Does not include crc
*/
public byte[] data = null;
/**
* A 4-byte CRC (Cyclic Redundancy Check) calculated on the preceding bytes
* in the chunk, including the chunk type code and chunk data fields, but
* not including the length field.
*/
private int crcval = 0;
/**
* @param len
* : data len
* @param idbytes
* : chunk type (deep copied)
* @param alloc
* : it true, the data array will be allocced
*/
public ChunkRaw(final int len, final byte[] idbytes, final boolean alloc) {
this.len = len;
System.arraycopy(idbytes, 0, this.idbytes, 0, 4);
if (alloc)
allocData();
}
private void allocData() {
if (data == null || data.length < len)
data = new byte[len];
}
/**
* this is called after setting data, before writing to os
*/
private int computeCrc() {
final CRC32 crcengine = PngHelperInternal.getCRC();
crcengine.reset();
crcengine.update(idbytes, 0, 4);
if (len > 0)
crcengine.update(data, 0, len); //
return (int) crcengine.getValue();
}
/**
* Computes the CRC and writes to the stream. If error, a
* PngjOutputException is thrown
*/
public void writeChunk(final OutputStream os) {
if (idbytes.length != 4)
throw new PngjOutputException("bad chunkid [" + ChunkHelper.toString(idbytes) + "]");
crcval = computeCrc();
PngHelperInternal.writeInt4(os, len);
PngHelperInternal.writeBytes(os, idbytes);
if (len > 0)
PngHelperInternal.writeBytes(os, data, 0, len);
PngHelperInternal.writeInt4(os, crcval);
}
/**
* position before: just after chunk id. positon after: after crc Data
* should be already allocated. Checks CRC Return number of byte read.
*/
public int readChunkData(final InputStream is, final boolean checkCrc) {
PngHelperInternal.readBytes(is, data, 0, len);
crcval = PngHelperInternal.readInt4(is);
if (checkCrc) {
final int crc = computeCrc();
if (crc != crcval)
throw new PngjBadCrcException("chunk: " + this + " crc calc=" + crc + " read=" + crcval);
}
return len + 4;
}
ByteArrayInputStream getAsByteStream() { // only the data
return new ByteArrayInputStream(data);
}
@Override
public String toString() {
return "chunkid=" + ChunkHelper.toString(idbytes) + " len=" + len;
}
}