package ar.com.hjg.pngj; import java.io.OutputStream; import ar.com.hjg.pngj.chunks.ChunkHelper; import ar.com.hjg.pngj.chunks.ChunkRaw; /** * Outputs a sequence of IDAT-like chunk, that is filled progressively until the * max chunk length is reached (or until * flush()) */ public class IDatChunkWriter { private static final int MAX_LEN_DEFAULT = 32768; // 32K rather arbitrary - data only private final OutputStream outputStream; private final int maxChunkLen; private byte[] buf; private int offset = 0; private int availLen; private long totalBytesWriten = 0; // including header+crc private int chunksWriten = 0; public IDatChunkWriter(OutputStream outputStream) { this(outputStream, 0); } public IDatChunkWriter(OutputStream outputStream, int maxChunkLength) { this.outputStream = outputStream; this.maxChunkLen = maxChunkLength > 0 ? maxChunkLength : MAX_LEN_DEFAULT; buf = new byte[maxChunkLen]; availLen = maxChunkLen - offset; postReset(); } public IDatChunkWriter(OutputStream outputStream, byte[] b) { this.outputStream = outputStream; this.buf = b != null ? b : new byte[MAX_LEN_DEFAULT]; this.maxChunkLen = b.length; availLen = maxChunkLen - offset; postReset(); } protected byte[] getChunkId() { return ChunkHelper.b_IDAT; } /** * Writes a chhunk if there is more than minLenToWrite. * * This is normally called internally, but can be called explicitly to force * flush. */ public final void flush() { if (offset > 0 && offset >= minLenToWrite()) { ChunkRaw c = new ChunkRaw(offset, getChunkId(), false); c.data = buf; c.writeChunk(outputStream); totalBytesWriten += c.len + 12; chunksWriten++; offset = 0; availLen = maxChunkLen; postReset(); } } public int getOffset() { return offset; } public int getAvailLen() { return availLen; } /** triggers an flush+reset if appropiate */ public void incrementOffset(int n) { offset += n; availLen -= n; if (availLen < 0) throw new PngjOutputException("Anomalous situation"); if (availLen == 0) { flush(); } } /** * this should rarely be used, the normal way (to avoid double copying) is * to get the buffer and write directly to it */ public void write(byte[] b, int o, int len) { while (len > 0) { int n = len <= availLen ? len : availLen; System.arraycopy(b, o, buf, offset, n); incrementOffset(n); len -= n; o += n; } } /** this will be called after reset */ protected void postReset() { // fdat could override this (and minLenToWrite) to add a prefix } protected int minLenToWrite() { return 1; } public void close() { flush(); offset = 0; buf = null; } /** * You can write directly to this buffer, using {@link #getOffset()} and * {@link #getAvailLen()}. You should call * {@link #incrementOffset(int)} inmediately after. */ public byte[] getBuf() { return buf; } public long getTotalBytesWriten() { return totalBytesWriten; } public int getChunksWriten() { return chunksWriten; } }