Skip to content

Commit 9e4f662

Browse files
committed
refactoring in progress
1 parent f11025a commit 9e4f662

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+337
-26262
lines changed

CMakeLists.txt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,5 @@ project(ImageLoading)
33

44
set(CMAKE_CXX_STANDARD 11)
55

6-
7-
file(GLOB_RECURSE FREEIMAGE_SOURCE zlib-master/*.c)
8-
file(GLOB_RECURSE FREEIMAGE_HEADERS zlib-master/*.h)
9-
10-
add_library(FreeImageLib STATIC ${FREEIMAGE_SOURCE} ${FREEIMAGE_HEADERS})
11-
12-
set(SOURCE_FILES main.cpp image.cpp image.h bitarray.cpp bitarray.h node.cpp node.h tree.cpp tree.h)
6+
set(SOURCE_FILES main.cpp image.cpp image.h bitarray.cpp bitarray.h node.h tree.cpp tree.h)
137
add_executable(ImageLoading ${SOURCE_FILES})
14-
15-
target_link_libraries(ImageLoading FreeImageLib)

image.cpp

Lines changed: 268 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,83 +3,293 @@
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

1823
Image::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

Comments
 (0)