aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunk.java
blob: 2df9fd1f32d94ac7a744834a1e84d4c4b0a7796a (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
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.PngjException;


// see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
public abstract class PngChunk {

	public final String id; // 4 letters
	public final boolean crit, pub, safe;
	private int lenori = -1; // merely informational, for read chunks

	private boolean writePriority = false; // for queued chunks
	protected final ImageInfo imgInfo;

	private int chunkGroup = -1; // chunk group where it was read or writen

	/**
	 * This static map defines which PngChunk class correspond to which ChunkID The client can add other chunks to this
	 * map statically, before reading
	 */
	public final static Map<String, Class<? extends PngChunk>> factoryMap = new HashMap<String, Class<? extends PngChunk>>();
	static {
		factoryMap.put(ChunkHelper.IDAT, PngChunkIDAT.class);
		factoryMap.put(ChunkHelper.IHDR, PngChunkIHDR.class);
		factoryMap.put(ChunkHelper.PLTE, PngChunkPLTE.class);
		factoryMap.put(ChunkHelper.IEND, PngChunkIEND.class);
		factoryMap.put(ChunkHelper.tEXt, PngChunkTEXT.class);
		factoryMap.put(ChunkHelper.iTXt, PngChunkITXT.class);
		factoryMap.put(ChunkHelper.zTXt, PngChunkZTXT.class);
		factoryMap.put(ChunkHelper.bKGD, PngChunkBKGD.class);
		factoryMap.put(ChunkHelper.gAMA, PngChunkGAMA.class);
		factoryMap.put(ChunkHelper.pHYs, PngChunkPHYS.class);
		factoryMap.put(ChunkHelper.iCCP, PngChunkICCP.class);
		factoryMap.put(ChunkHelper.tIME, PngChunkTIME.class);
		factoryMap.put(ChunkHelper.tRNS, PngChunkTRNS.class);
		factoryMap.put(ChunkHelper.cHRM, PngChunkCHRM.class);
		factoryMap.put(ChunkHelper.sBIT, PngChunkSBIT.class);
		factoryMap.put(ChunkHelper.sRGB, PngChunkSRGB.class);
		factoryMap.put(ChunkHelper.hIST, PngChunkHIST.class);
		factoryMap.put(ChunkHelper.sPLT, PngChunkSPLT.class);
	}

	protected PngChunk(String id, ImageInfo imgInfo) {
		this.id = id;
		this.imgInfo = imgInfo;
		this.crit = ChunkHelper.isCritical(id);
		this.pub = ChunkHelper.isPublic(id);
		this.safe = ChunkHelper.isSafeToCopy(id);
	}

	public abstract ChunkRaw createChunk();

	public abstract void parseFromChunk(ChunkRaw c);

	// override to make deep copy from read data to write
	public abstract void cloneDataFromRead(PngChunk other);

	@SuppressWarnings("unchecked")
	public static <T extends PngChunk> T cloneChunk(T chunk, ImageInfo info) {
		PngChunk cn = factoryFromId(chunk.id, info);
		if (cn.getClass() != chunk.getClass())
			throw new PngjException("bad class cloning chunk: " + cn.getClass() + " " + chunk.getClass());
		cn.cloneDataFromRead(chunk);
		return (T) cn;
	}

	public static PngChunk factory(ChunkRaw chunk, ImageInfo info) {
		PngChunk c = factoryFromId(ChunkHelper.toString(chunk.idbytes), info);
		c.lenori = chunk.len;
		c.parseFromChunk(chunk);
		return c;
	}

	public static PngChunk factoryFromId(String cid, ImageInfo info) {
		PngChunk chunk = null;
		try {
			Class<? extends PngChunk> cla = factoryMap.get(cid);
			if (cla != null) {
				Constructor<? extends PngChunk> constr = cla.getConstructor(ImageInfo.class);
				chunk = constr.newInstance(info);
			}
		} catch (Exception e) {
			// this can happend for unkown chunks
		}
		if (chunk == null)
			chunk = new PngChunkUNKNOWN(cid, info);
		return chunk;
	}

	protected ChunkRaw createEmptyChunk(int len, boolean alloc) {
		ChunkRaw c = new ChunkRaw(len, ChunkHelper.toBytes(id), alloc);
		return c;
	}

	@Override
	public String toString() {
		return "chunk id= " + id + " (" + lenori + ") c=" + getClass().getSimpleName();
	}

	void setPriority(boolean highPrioriy) {
		writePriority = highPrioriy;
	}

	void write(OutputStream os) {
		ChunkRaw c = createChunk();
		if (c == null)
			throw new PngjException("null chunk ! creation failed for " + this);
		c.writeChunk(os);
	}

	public boolean isWritePriority() {
		return writePriority;
	}

	/** must be overriden - only relevant for ancillary chunks */
	public boolean allowsMultiple() {
		return false; // override if allows multiple ocurrences
	}

	/** mustGoBeforeXX/After must be overriden - only relevant for ancillary chunks */
	public boolean mustGoBeforeIDAT() {
		return false;
	}

	public boolean mustGoBeforePLTE() {
		return false;
	}

	public boolean mustGoAfterPLTE() {
		return false;
	}

	static boolean isKnown(String id) {
		return factoryMap.containsKey(id);
	}

	public int getChunkGroup() {
		return chunkGroup;
	}

	public void setChunkGroup(int chunkGroup) {
		this.chunkGroup = chunkGroup;
	}

}