Skip to main content
summaryrefslogtreecommitdiffstats
blob: 1800a2415bd03385d928359bc45463959bfd0c76 (plain) (blame)
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
package org.eclipse.swt.internal.image;

import java.io.*;
import org.eclipse.swt.*;

/*
 * Licensed Materials - Property of IBM,
 * (c) Copyright IBM Corp. 1998, 2000  All Rights Reserved
 */

public class PngDecodingDataStream {
	PngIdatChunk currentChunk;
	PngChunkReader chunkReader;
	byte currentByte;
	int nextByteIndex;
	int nextBitIndex;
	
	PngLzBlockReader lzBlockReader;
	int adlerValue;
	
	static final int PRIME = 65521;
	static final int MAX_BIT = 7;		
	
PngDecodingDataStream(PngIdatChunk idatChunk, PngChunkReader chunkReader) {
	super();
	this.currentChunk = idatChunk;
	this.chunkReader = chunkReader;
	nextByteIndex = 0;
	nextBitIndex = MAX_BIT + 1;
	adlerValue = 1;
	lzBlockReader = new PngLzBlockReader(this);
	readCompressedDataHeader();
	lzBlockReader.readNextBlockHeader();
}

/**
 * This method should be called when the image decoder thinks
 * that all of the compressed image data has been read. This
 * method will ensure that the next data value is an end of 
 * block marker. If there are more blocks after this one,
 * the method will read them and ensure that they are empty.
 */
void assertImageDataAtEnd() {
	lzBlockReader.assertCompressedDataAtEnd();
}

int getNextIdatBits(int length) {
	int value = 0;
	for (int i = 0; i < length; i++) {
		value |= (getNextIdatBit() << i);
	}
	return value;
}

byte getNextIdatBit() {
	if (nextBitIndex > MAX_BIT) {
		currentByte = getNextIdatByte();
		nextBitIndex = 0;
	}	
	int mask = 1 << nextBitIndex;
	nextBitIndex++;
	return ((currentByte & mask) > 0) ? (byte) 1 : (byte) 0;	
}

private PngIdatChunk getNextChunk() {
	PngChunk chunk = chunkReader.readNextChunk();
	if (chunk == null) error();
	if (chunk.getChunkType() != PngChunk.CHUNK_IDAT) error(); 
	return (PngIdatChunk) chunk;
}

byte getNextIdatByte() {
	if (nextByteIndex > currentChunk.getLength() - 1) {
		currentChunk = getNextChunk();
		nextByteIndex = 0;
	}
	byte nextByte = currentChunk.getDataByteAtOffset(nextByteIndex);
	nextByteIndex++;
	nextBitIndex = MAX_BIT + 1;
	return nextByte;
}

private void updateAdler(byte value) {
	int low = adlerValue & 0xFFFF;
	int high = (adlerValue >> 16) & 0xFFFF;
	int valueInt = value & 0xFF;
	low = (low + valueInt) % PRIME;
	high = (low + high) % PRIME;
	adlerValue = (high << 16) | low;
}

byte getNextDecodedByte() {
	byte nextDecodedByte = lzBlockReader.getNextByte();
	updateAdler(nextDecodedByte);
	return nextDecodedByte;
}

void error() {
	SWT.error(SWT.ERROR_INVALID_IMAGE);
}

private void readCompressedDataHeader() {
	byte headerByte1 = getNextIdatByte();
	byte headerByte2 = getNextIdatByte();
	
	int number = (headerByte1 << 8) | (headerByte2 & 0xFF);
	if (number % 31 != 0) error();
	
	int compressionMethod = headerByte1 & 0x0F;
	if (compressionMethod != 8) error();
	
	int windowSizeHint = (headerByte1 & 0xF0) >> 4;
	if (windowSizeHint > 7) error();
	int windowSize = (1 << (windowSizeHint + 8));
	lzBlockReader.setWindowSize(windowSize);
	
	int dictionary = (headerByte2 & (1 << 5));
	if (dictionary != 0) error();
	
	int compressionLevel = (headerByte2 & 0xC0) >> 6;
}

void checkAdler() {
	int storedAdler = ((getNextIdatByte() & 0xFF) << 24)
		| ((getNextIdatByte() & 0xFF) << 16)
		| ((getNextIdatByte() & 0xFF) << 8)
		| (getNextIdatByte() & 0xFF);
	if (storedAdler != adlerValue) error();
}

}

Back to the top