33//
44
55#include " image.h"
6+ #include < sstream>
7+ #include < fstream>
8+ #include " bitarray.h"
9+ #include " tree.h"
610
7- unsigned char reversed (unsigned char value) {
8- unsigned char t = value;
9- for (int i = sizeof (value) * 8 -1 ; i; i--)
10- {
11- value >>= 1 ;
12- t <<= 1 ;
13- t |= value & 1 ;
14- }
15- return t;
16- }
11+ // The order of which symbol comes first in the 3-bit lengths specifications for the first huffman tree
12+ // (the meta tree, aka the one that is used to create the literal and distance trees)
13+ static const unsigned short metaTreeOrder[19 ] = {16 , 17 , 18 , 0 , 8 , 7 , 9 , 6 , 10 , 5 , 11 , 4 , 12 , 3 , 13 , 2 , 14 , 1 , 15 };
14+ // Base length of the length codes
15+ static const unsigned short lengths[29 ] = {3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 13 , 15 , 17 , 19 , 23 , 27 , 31 , 35 , 43 , 51 , 59 , 67 , 83 , 99 , 115 , 131 , 163 , 195 , 227 , 258 };
16+ // How many extra bits are needed to specify the actual length for the length code
17+ static const unsigned short lengthsExtraBits[29 ] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 2 , 3 , 3 , 3 , 3 , 4 , 4 , 4 , 4 , 5 , 5 , 5 , 5 , 0 };
18+ // Base offset of the offset codes
19+ static const unsigned int offsets[30 ] = {1 , 2 , 3 , 4 , 5 , 7 , 9 , 13 , 17 , 25 , 33 , 49 , 65 , 97 , 129 , 193 , 257 , 385 , 513 , 769 , 1025 , 1537 , 2049 , 3073 , 4097 , 6145 , 8193 , 12289 , 16385 , 24577 };
20+ // How many extra bits are needed to specify the actual offset for the offset code
21+ static const unsigned int offsetsExtraBits[30 ] = {0 , 0 , 0 , 0 , 1 , 1 , 2 , 2 , 3 , 3 , 4 , 4 , 5 , 5 , 6 , 6 , 7 , 7 , 8 , 8 , 9 , 9 , 10 , 10 , 11 , 11 , 12 , 12 , 13 , 13 };
1722
1823Image::Image (const std::string& path) {
19- init (path);
20- }
21-
22- void Image::init (const std::string& path) {
23- std::ifstream stream (path, std::ifstream::in | std::ifstream::ate | std::ifstream::binary);
24- auto size = (unsigned int ) stream.tellg ();
25- if (!stream.good ()) {
26- LOG (" FAILED TO OPEN FILE: " << path);
27- size = 0 ;
24+ std::vector<Chunk> chunks;
25+ {
26+ // Loads the image file and formats the data in chunks (as per the PNG specification)
27+ std::ifstream stream (path, std::ifstream::in | std::ifstream::ate | std::ifstream::binary);
28+ auto size = (unsigned int ) stream.tellg ();
29+ if (!stream.good ()) {
30+ pixels = new Pixel (0 , 0 , 0 , 0 );
31+ format.width = 1 ;
32+ format.height = 1 ;
33+ format.bitDepth = 8 ;
34+ format.colorFormat = IMAGE_FORMAT_RGBA;
35+ }
36+ stream.seekg (0 , std::ifstream::beg);
37+
38+ unsigned char data[size];
39+ unsigned int position = 0 ;
40+ char in;
41+ while (stream.get (in))
42+ data[position++] = (unsigned char ) in;
43+
44+ stream.close ();
45+
46+ loadChunks (chunks, data, size);
47+
48+ // Sets the format of the image
49+ std::vector<unsigned char >& dataIDHR = chunks[0 ].data ; // the IDAT chunk
50+ format.width = dataIDHR[0 ] << 24 | dataIDHR[1 ] << 16 | dataIDHR[2 ] << 8 | dataIDHR[3 ];
51+ format.height = dataIDHR[4 ] << 24 | dataIDHR[5 ] << 16 | dataIDHR[6 ] << 8 | dataIDHR[7 ];
52+ format.bitDepth = dataIDHR[8 ];
53+ format.colorFormat = dataIDHR[9 ];
54+ if (format.colorFormat == 3 ) {
55+ pixels = new Pixel (0 , 0 , 0 , 0 );
56+ format.width = 1 ;
57+ format.height = 1 ;
58+ format.bitDepth = 8 ;
59+ format.colorFormat = IMAGE_FORMAT_RGBA;
60+ }
2861 }
29- stream.seekg (0 , std::ifstream::beg);
3062
31- unsigned char data[size];
63+ // figures out how many IDAT chunks exist
64+ unsigned int firstIDAT = 0 ;
65+ while (chunks[++firstIDAT].type != " IDAT" );
66+ unsigned int lastIDAT = firstIDAT;
67+ while (++lastIDAT < chunks.size () && chunks[lastIDAT].type == " IDAT" );
3268
33- unsigned int position = 0 ;
34- char in;
35- while (stream.get (in))
36- data[position++] = (unsigned char ) in;
69+ Chunk firstPixelChunk = chunks[firstIDAT];
70+ std::vector<unsigned char >& firstCompressedData = firstPixelChunk.data ;
71+ // todo: use dict and stuff
72+ // BitArray flagBits(firstCompressedData[1], 8);
73+ // bool fDict = flagBits.read(5, 1) != 0;
3774
38- loadChunks (data, size);
39- formatIHDR ();
75+ unsigned int pos = 0 ; // The current position in the bitstream
76+ // The bit stream containing all the bits of the compressed data and then adds all the data to the bitstream
77+ BitArray bitStream;
78+ for (unsigned int i = 2 ; i < firstCompressedData.size (); i++)
79+ bitStream.pushBack (firstCompressedData[i]);
80+ for (unsigned int index = firstIDAT + 1 ; index < lastIDAT; index++)
81+ for (unsigned char i : chunks[index].data )
82+ bitStream.pushBack (i);
4083
41- stream.close ();
42- }
43-
44- void Image::formatIHDR () {
45- auto & data = chunks[0 ]->data ;
84+ std::vector<unsigned int > fixedTreeSymbols;
85+ fixedTreeSymbols.reserve (288 );
86+ std::vector<unsigned int > fixedTreeLengths;
87+ fixedTreeLengths.reserve (288 );
88+ for (unsigned int i = 0 ; i < 288 ; i++) {
89+ fixedTreeSymbols.push_back (i);
90+ if (i <= 143 )
91+ fixedTreeLengths.push_back (8 );
92+ else if (i <= 255 )
93+ fixedTreeLengths.push_back (9 );
94+ else if (i <= 279 )
95+ fixedTreeLengths.push_back (7 );
96+ else // if (i <= 287)
97+ fixedTreeLengths.push_back (8 );
98+ }
99+ Tree fixedTree = Tree (fixedTreeSymbols, fixedTreeLengths);
46100
47- format.width = (data[0 ] << 24 ) | (data[1 ] << 16 ) | (data[2 ] << 8 ) | (data[3 ]);
48- format.height = (data[4 ] << 24 ) | (data[5 ] << 16 ) | (data[6 ] << 8 ) | (data[7 ]);
49- format.bitDepth = data[8 ];
50- format.colorType = data[9 ];
51- format.compressionMethod = data[10 ];
52- format.filterMethod = data[11 ];
53- format.interlaceMethod = data[12 ];
101+ std::vector<unsigned char > dataStream;
102+ dataStream.reserve ((1 + format.width ) * format.height );
103+
104+ bool lastBlock = false ;
105+ while (!lastBlock) {
106+ lastBlock = bitStream.read (pos, 1 ) == 1 ;
107+ pos += 1 ;
108+ unsigned short compressionMethod = (unsigned short ) bitStream.read (pos, 2 );
109+ pos += 2 ;
110+
111+ if (compressionMethod == 1 ) {
112+ while (true ) {
113+ unsigned int code = fixedTree.uncompressOneCode (bitStream, pos, &pos);
114+
115+
116+ if (code < 256 ) {
117+ dataStream.emplace_back (code);
118+ } else if (code == 256 ) {
119+ break ;
120+ } else {
121+ code -= 257 ;
122+ unsigned int length = lengths[code];
123+ length += bitStream.read (pos, lengthsExtraBits[code]);
124+ pos += lengthsExtraBits[code];
125+
126+ unsigned int offsetCode = bitStream.read (pos, 5 , true ); // reads inverted
127+ pos += 5 ;
128+ unsigned int offset = offsets[offsetCode];
129+ offset += bitStream.read (pos, offsetsExtraBits[offsetCode]);
130+ pos += offsetsExtraBits[offsetCode];
131+
132+ for (int i = 0 ; i < length; i++) {
133+ dataStream.emplace_back (dataStream[dataStream.size () - offset]);
134+ }
135+ }
136+ }
137+ } else if (compressionMethod == 2 ) {
138+ // Reads the 14 bit header of the compressed block
139+ unsigned int hlit = bitStream.read (pos, 5 ) + 257 ;
140+ unsigned int hdist = bitStream.read (pos + 5 , 5 ) + 1 ;
141+ unsigned int hlen = bitStream.read (pos + 10 , 4 ) + 4 ;
142+ pos += 14 ;
143+
144+ // Creates the huffman tree that reads the length of the literal and distance trees
145+ std::vector<unsigned int > metaTreeSymbols;
146+ metaTreeSymbols.reserve (19 );
147+ std::vector<unsigned int > metaTreeLengths (19 );
148+ {
149+ unsigned int i = 0 ;
150+ for (; i < hlen; i++) {
151+ metaTreeSymbols.push_back (i);
152+ metaTreeLengths[metaTreeOrder[i]] = bitStream.read (pos, 3 );
153+ pos += 3 ;
154+ }
155+ for (; i < 19 ; i++) {
156+ metaTreeSymbols.push_back (i);
157+ metaTreeLengths[metaTreeOrder[i]] = 0 ;
158+ }
159+ }
160+ Tree metaTree (metaTreeSymbols, metaTreeLengths);
161+
162+ std::vector<unsigned int > litAndDistLengths;
163+ litAndDistLengths.reserve (hlit + hdist);
164+
165+ while (litAndDistLengths.size () < hlit + hdist) {
166+ unsigned int code = metaTree.uncompressOneCode (bitStream, pos, &pos);
167+
168+ if (code <= 15 ) {
169+ litAndDistLengths.push_back (code);
170+ } else if (code == 16 ) {
171+ unsigned int extra = bitStream.read (pos, 2 );
172+ pos += 2 ;
173+
174+ unsigned long long int copyPos = litAndDistLengths.size () - 1 ;
175+ for (unsigned int i = 0 ; i < 3 + extra; i++) {
176+ litAndDistLengths.push_back (litAndDistLengths[copyPos]);
177+ }
178+ } else if (code == 17 ) {
179+ unsigned int extra = bitStream.read (pos, 3 );
180+ pos += 3 ;
181+
182+ for (unsigned int i = 0 ; i < 3 + extra; i++) {
183+ litAndDistLengths.emplace_back (0 );
184+ }
185+ } else /* if (code == 18)*/ {
186+ unsigned int extra = bitStream.read (pos, 7 );
187+ pos += 7 ;
188+
189+ for (unsigned int i = 0 ; i < 11 + extra; i++) {
190+ litAndDistLengths.emplace_back (0 );
191+ }
192+ }
193+ }
194+
195+ std::vector<unsigned int > litSymbols;
196+ litSymbols.reserve (hlit);
197+ std::vector<unsigned int > distSymbols;
198+ distSymbols.reserve (hdist);
199+
200+ for (unsigned int i = 0 ; i < hlit; i++)
201+ litSymbols.push_back (i);
202+ for (unsigned int i = 0 ; i < hdist; i++)
203+ distSymbols.push_back (i);
204+
205+ std::vector<unsigned int > litLengths;
206+ litLengths.reserve (hlit);
207+ std::vector<unsigned int > distLengths;
208+ distLengths.reserve (hdist);
209+ for (unsigned int i = 0 ; i < hlit; i++)
210+ litLengths.emplace_back (litAndDistLengths[i]);
211+ for (unsigned int i = 0 ; i < hdist; i++)
212+ distLengths.emplace_back (litAndDistLengths[hlit + i]);
213+
214+ Tree literalTree (litSymbols, litLengths);
215+ Tree distTree (distSymbols, distLengths);
216+
217+ while (true ) {
218+ unsigned int code = literalTree.uncompressOneCode (bitStream, pos, &pos);
219+
220+ if (code < 256 ) {
221+ dataStream.push_back ((unsigned char ) code);
222+ } else if (code == 256 ) {
223+ break ;
224+ } else {
225+ code -= 257 ;
226+ unsigned int length = lengths[code];
227+ length += bitStream.read (pos, lengthsExtraBits[code]);
228+ pos += lengthsExtraBits[code];
229+
230+ unsigned int offsetCode = distTree.uncompressOneCode (bitStream, pos, &pos);
231+
232+ unsigned int offset = offsets[offsetCode];
233+ offset += bitStream.read (pos, offsetsExtraBits[offsetCode], false );
234+ pos += offsetsExtraBits[offsetCode];
235+
236+ for (int i = 0 ; i < length; i++) {
237+ dataStream.push_back (dataStream[dataStream.size () - offset]);
238+ }
239+ }
240+ }
241+ } else /* if (compressionMethod == 0)*/ {
242+ unsigned int offsetFromByte = pos & 0x7 ;
243+ if (offsetFromByte != 0 )
244+ pos += (8 - offsetFromByte);
245+
246+ unsigned int length = bitStream.read (pos, 16 );
247+ pos += 32 ;
248+
249+ for (unsigned int i = 0 ; i < length; i++) {
250+ dataStream.emplace_back (bitStream.read (pos, 8 ));
251+ pos += 8 ;
252+ }
253+ }
254+ }
255+
256+ // todo: add pixel creation for types 0, 2 and 4
257+
258+ pixels = new Pixel[format.width * format.height ];
259+
260+ unsigned int actualWidth = 1 + format.width * 4 ;
261+ for (unsigned int y = 0 ; y < format.height ; y++) {
262+ for (unsigned int x = 0 ; x < format.width ; x++) {
263+ unsigned int i = 1 + 4 * x + y * actualWidth;
264+ pixels[x + y * format.width ].r = (unsigned char ) dataStream[i + 0 ];
265+ pixels[x + y * format.width ].g = (unsigned char ) dataStream[i + 1 ];
266+ pixels[x + y * format.width ].b = (unsigned char ) dataStream[i + 2 ];
267+ pixels[x + y * format.width ].a = (unsigned char ) dataStream[i + 3 ];
268+ }
269+ }
54270}
55271
56- std::vector<unsigned char > Image::extractCompressedPixelData (const Chunk& chunk) {
57- std::vector<unsigned char > compressedData;
58- compressedData.reserve (chunk.length );
59- for (int i = 0 ; i < chunk.length ; i++)
60- compressedData.push_back (chunk.data [i]);
61-
62- return compressedData;
272+ Image::~Image () {
273+ delete[] pixels;
63274}
64275
65- void Image::loadChunks (const unsigned char * data, unsigned int size) {
276+ void Image::loadChunks (std::vector<Chunk>& chunks, const unsigned char * data, unsigned int size) {
66277 unsigned int offset = 8 ;
67- while (offset < size) {
278+ while (offset < size)
68279 chunks.push_back (loadChunk (data, offset));
69- }
70280}
71281
72- Chunk* Image::loadChunk (const unsigned char * data, unsigned int & offset) {
282+ Chunk Image::loadChunk (const unsigned char * data, unsigned int & offset) {
73283 unsigned int chunkLength = (data[offset + 0 ] << 24 ) | (data[offset + 1 ] << 16 ) | (data[offset + 2 ] << 8 ) | (data[offset + 3 ]);
74284 offset += 4 ;
75285
76- char s [5 ];
77- s [0 ] = data[offset + 0 ];
78- s [1 ] = data[offset + 1 ];
79- s [2 ] = data[offset + 2 ];
80- s [3 ] = data[offset + 3 ];
81- s [4 ] = 0 ;
82- std::string chunkType (s );
286+ char charArray [5 ];
287+ charArray [0 ] = data[offset + 0 ];
288+ charArray [1 ] = data[offset + 1 ];
289+ charArray [2 ] = data[offset + 2 ];
290+ charArray [3 ] = data[offset + 3 ];
291+ charArray [4 ] = 0 ;
292+ std::string chunkType (charArray );
83293 offset += 4 ;
84294
85295 std::vector<unsigned char > chunkData;
@@ -93,5 +303,5 @@ Chunk* Image::loadChunk(const unsigned char* data, unsigned int& offset) {
93303 unsigned int chunkCRC = (data[offset + 0 ] << 24 ) | (data[offset + 1 ] << 16 ) | (data[offset + 2 ] << 8 ) | (data[offset + 3 ]);
94304 offset += 4 ;
95305
96- return new Chunk (chunkLength, chunkType, chunkData, chunkCRC);
306+ return Chunk (chunkLength, chunkType, chunkData, chunkCRC);
97307}
0 commit comments