aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java
blob: 66c4b49f032ba19cc91c51557f11d16c2cb963a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package jogamp.opengl.util.pngj;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.CRC32;

import jogamp.opengl.util.pngj.chunks.ChunkHelper;


/**
 * Reads IDAT chunks
 */
class PngIDatChunkInputStream extends InputStream {
	private final InputStream inputStream;
	private final CRC32 crcEngine;
	private int lenLastChunk;
	private byte[] idLastChunk = new byte[4];
	private int toReadThisChunk = 0;
	private boolean ended = false;
	private long offset; // offset inside inputstream

	// just informational
	static class IdatChunkInfo {
		public final int len;
		public final int offset;

		private IdatChunkInfo(int len, int offset) {
			this.len = len;
			this.offset = offset;
		}
	}

	List<IdatChunkInfo> foundChunksInfo = new ArrayList<IdatChunkInfo>();

	/**
	 * Constructor must be called just after reading length and id of first IDAT chunk
	 **/
	PngIDatChunkInputStream(InputStream iStream, int lenFirstChunk, int offset) {
		this.offset = (long) offset;
		inputStream = iStream;
		crcEngine = new CRC32();
		this.lenLastChunk = lenFirstChunk;
		toReadThisChunk = lenFirstChunk;
		// we know it's a IDAT
		System.arraycopy(ChunkHelper.b_IDAT, 0, idLastChunk, 0, 4);
		crcEngine.update(idLastChunk, 0, 4);
		foundChunksInfo.add(new IdatChunkInfo(lenLastChunk, offset - 8));
		// PngHelper.logdebug("IDAT Initial fragment: len=" + lenLastChunk);
		if (this.lenLastChunk == 0)
			endChunkGoForNext(); // rare, but...
	}

	/**
	 * does NOT close the associated stream!
	 */
	@Override
	public void close() throws IOException {
		super.close(); // nothing
	}

	private void endChunkGoForNext() {
		// Called after readging the last byte of chunk
		// Checks CRC, and read ID from next CHUNK
		// Those values are left in idLastChunk / lenLastChunk
		// Skips empty IDATS
		do {
			int crc = PngHelper.readInt4(inputStream); //
			offset += 4;
			int crccalc = (int) crcEngine.getValue();
			if (lenLastChunk > 0 && crc != crccalc)
				throw new PngjBadCrcException("error reading idat; offset: " + offset);
			crcEngine.reset();
			lenLastChunk = PngHelper.readInt4(inputStream);
			if (lenLastChunk < 0)
				throw new PngjInputException("invalid len for chunk: " + lenLastChunk);
			toReadThisChunk = lenLastChunk;
			PngHelper.readBytes(inputStream, idLastChunk, 0, 4);
			offset += 8;
			ended = !Arrays.equals(idLastChunk, ChunkHelper.b_IDAT);
			if (!ended) {
				foundChunksInfo.add(new IdatChunkInfo(lenLastChunk, (int) (offset - 8)));
				crcEngine.update(idLastChunk, 0, 4);
			}
			// PngHelper.logdebug("IDAT ended. next len= " + lenLastChunk + " idat?" +
			// (!ended));
		} while (lenLastChunk == 0 && !ended);
		// rarely condition is true (empty IDAT ??)
	}

	/**
	 * sometimes last row read does not fully consumes the chunk here we read the reamaing dummy bytes
	 */
	void forceChunkEnd() {
		if (!ended) {
			byte[] dummy = new byte[toReadThisChunk];
			PngHelper.readBytes(inputStream, dummy, 0, toReadThisChunk);
			crcEngine.update(dummy, 0, toReadThisChunk);
			endChunkGoForNext();
		}
	}

	/**
	 * This can return less than len, but never 0 Returns -1 if "pseudo file" ended prematurely. That is our error.
	 */
	@Override
	public int read(byte[] b, int off, int len) throws IOException {
		if (toReadThisChunk == 0)
			throw new RuntimeException("this should not happen");
		int n = inputStream.read(b, off, len >= toReadThisChunk ? toReadThisChunk : len);
		if (n > 0) {
			crcEngine.update(b, off, n);
			this.offset += n;
			toReadThisChunk -= n;
		}
		if (toReadThisChunk == 0) { // end of chunk: prepare for next
			endChunkGoForNext();
		}
		return n;
	}

	@Override
	public int read(byte[] b) throws IOException {
		return this.read(b, 0, b.length);
	}

	@Override
	public int read() throws IOException {
		// PngHelper.logdebug("read() should go here");
		// inneficient - but this should be used rarely
		byte[] b1 = new byte[1];
		int r = this.read(b1, 0, 1);
		return r < 0 ? -1 : (int) b1[0];
	}

	int getLenLastChunk() {
		return lenLastChunk;
	}

	byte[] getIdLastChunk() {
		return idLastChunk;
	}

	long getOffset() {
		return offset;
	}

	boolean isEnded() {
		return ended;
	}
}