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. * <p> * Short lived object, to be created while serialing/deserializing Do not reuse * it for different chunks. <br> * 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; } }