Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/nall/png.hpp
2 views
1
#ifndef NALL_PNG_HPP
2
#define NALL_PNG_HPP
3
4
//PNG image decoder
5
//author: byuu
6
7
#include <nall/inflate.hpp>
8
#include <nall/string.hpp>
9
10
namespace nall {
11
12
struct png {
13
//colorType:
14
//0 = L
15
//2 = R,G,B
16
//3 = P
17
//4 = L,A
18
//6 = R,G,B,A
19
struct Info {
20
unsigned width;
21
unsigned height;
22
unsigned bitDepth;
23
unsigned colorType;
24
unsigned compressionMethod;
25
unsigned filterType;
26
unsigned interlaceMethod;
27
28
unsigned bytesPerPixel;
29
unsigned pitch;
30
31
uint8_t palette[256][3];
32
} info;
33
34
uint8_t *data;
35
unsigned size;
36
37
inline bool decode(const string &filename);
38
inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
39
inline unsigned readbits(const uint8_t *&data);
40
unsigned bitpos;
41
42
inline png();
43
inline ~png();
44
45
protected:
46
enum class FourCC : unsigned {
47
IHDR = 0x49484452,
48
PLTE = 0x504c5445,
49
IDAT = 0x49444154,
50
IEND = 0x49454e44,
51
};
52
53
inline unsigned interlace(unsigned pass, unsigned index);
54
inline unsigned inflateSize();
55
inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
56
inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
57
inline unsigned read(const uint8_t *data, unsigned length);
58
};
59
60
bool png::decode(const string &filename) {
61
uint8_t *data;
62
unsigned size;
63
if(file::read(filename, data, size) == false) return false;
64
bool result = decode(data, size);
65
delete[] data;
66
return result;
67
}
68
69
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
70
if(sourceSize < 8) return false;
71
if(read(sourceData + 0, 4) != 0x89504e47) return false;
72
if(read(sourceData + 4, 4) != 0x0d0a1a0a) return false;
73
74
uint8_t *compressedData = 0;
75
unsigned compressedSize = 0;
76
77
unsigned offset = 8;
78
while(offset < sourceSize) {
79
unsigned length = read(sourceData + offset + 0, 4);
80
unsigned fourCC = read(sourceData + offset + 4, 4);
81
unsigned checksum = read(sourceData + offset + 8 + length, 4);
82
83
if(fourCC == (unsigned)FourCC::IHDR) {
84
info.width = read(sourceData + offset + 8, 4);
85
info.height = read(sourceData + offset + 12, 4);
86
info.bitDepth = read(sourceData + offset + 16, 1);
87
info.colorType = read(sourceData + offset + 17, 1);
88
info.compressionMethod = read(sourceData + offset + 18, 1);
89
info.filterType = read(sourceData + offset + 19, 1);
90
info.interlaceMethod = read(sourceData + offset + 20, 1);
91
92
if(info.bitDepth == 0 || info.bitDepth > 16) return false;
93
if(info.bitDepth & (info.bitDepth - 1)) return false; //not a power of two
94
if(info.compressionMethod != 0) return false;
95
if(info.filterType != 0) return false;
96
if(info.interlaceMethod != 0 && info.interlaceMethod != 1) return false;
97
98
switch(info.colorType) {
99
case 0: info.bytesPerPixel = info.bitDepth * 1; break; //L
100
case 2: info.bytesPerPixel = info.bitDepth * 3; break; //R,G,B
101
case 3: info.bytesPerPixel = info.bitDepth * 1; break; //P
102
case 4: info.bytesPerPixel = info.bitDepth * 2; break; //L,A
103
case 6: info.bytesPerPixel = info.bitDepth * 4; break; //R,G,B,A
104
default: return false;
105
}
106
107
if(info.colorType == 2 || info.colorType == 4 || info.colorType == 6)
108
if(info.bitDepth != 8 && info.bitDepth != 16) return false;
109
if(info.colorType == 3 && info.bitDepth == 16) return false;
110
111
info.bytesPerPixel = (info.bytesPerPixel + 7) / 8;
112
info.pitch = (int)info.width * info.bytesPerPixel;
113
}
114
115
if(fourCC == (unsigned)FourCC::PLTE) {
116
if(length % 3) return false;
117
for(unsigned n = 0, p = offset + 8; n < length / 3; n++) {
118
info.palette[n][0] = sourceData[p++];
119
info.palette[n][1] = sourceData[p++];
120
info.palette[n][2] = sourceData[p++];
121
}
122
}
123
124
if(fourCC == (unsigned)FourCC::IDAT) {
125
compressedData = (uint8_t*)realloc(compressedData, compressedSize + length);
126
memcpy(compressedData + compressedSize, sourceData + offset + 8, length);
127
compressedSize += length;
128
}
129
130
if(fourCC == (unsigned)FourCC::IEND) {
131
break;
132
}
133
134
offset += 4 + 4 + length + 4;
135
}
136
137
unsigned interlacedSize = inflateSize();
138
uint8_t *interlacedData = new uint8_t[interlacedSize];
139
140
bool result = inflate(interlacedData, interlacedSize, compressedData + 2, compressedSize - 6);
141
delete[] compressedData;
142
143
if(result == false) {
144
delete[] interlacedData;
145
return false;
146
}
147
148
size = info.width * info.height * info.bytesPerPixel;
149
data = new uint8_t[size];
150
151
if(info.interlaceMethod == 0) {
152
if(filter(data, interlacedData, info.width, info.height) == false) {
153
delete[] interlacedData;
154
delete[] data;
155
data = 0;
156
return false;
157
}
158
} else {
159
const uint8_t *passData = interlacedData;
160
for(unsigned pass = 0; pass < 7; pass++) {
161
if(deinterlace(passData, pass) == false) {
162
delete[] interlacedData;
163
delete[] data;
164
data = 0;
165
return false;
166
}
167
}
168
}
169
170
delete[] interlacedData;
171
return true;
172
}
173
174
unsigned png::interlace(unsigned pass, unsigned index) {
175
static const unsigned data[7][4] = {
176
//x-distance, y-distance, x-origin, y-origin
177
{ 8, 8, 0, 0 },
178
{ 8, 8, 4, 0 },
179
{ 4, 8, 0, 4 },
180
{ 4, 4, 2, 0 },
181
{ 2, 4, 0, 2 },
182
{ 2, 2, 1, 0 },
183
{ 1, 2, 0, 1 },
184
};
185
return data[pass][index];
186
}
187
188
unsigned png::inflateSize() {
189
if(info.interlaceMethod == 0) {
190
return info.width * info.height * info.bytesPerPixel + info.height;
191
}
192
193
unsigned size = 0;
194
for(unsigned pass = 0; pass < 7; pass++) {
195
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
196
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
197
unsigned width = (info.width + (xd - xo - 1)) / xd;
198
unsigned height = (info.height + (yd - yo - 1)) / yd;
199
if(width == 0 || height == 0) continue;
200
size += width * height * info.bytesPerPixel + height;
201
}
202
return size;
203
}
204
205
bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
206
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
207
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
208
unsigned width = (info.width + (xd - xo - 1)) / xd;
209
unsigned height = (info.height + (yd - yo - 1)) / yd;
210
if(width == 0 || height == 0) return true;
211
212
unsigned outputSize = width * height * info.bytesPerPixel;
213
uint8_t *outputData = new uint8_t[outputSize];
214
bool result = filter(outputData, inputData, width, height);
215
216
const uint8_t *rd = outputData;
217
for(unsigned y = yo; y < info.height; y += yd) {
218
uint8_t *wr = data + y * info.pitch;
219
for(unsigned x = xo; x < info.width; x += xd) {
220
for(unsigned b = 0; b < info.bytesPerPixel; b++) {
221
wr[x * info.bytesPerPixel + b] = *rd++;
222
}
223
}
224
}
225
226
inputData += outputSize + height;
227
delete[] outputData;
228
return result;
229
}
230
231
bool png::filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height) {
232
uint8_t *wr = outputData;
233
const uint8_t *rd = inputData;
234
int bpp = info.bytesPerPixel, pitch = width * bpp;
235
for(int y = 0; y < height; y++) {
236
uint8_t filter = *rd++;
237
238
switch(filter) {
239
case 0x00: //None
240
for(int x = 0; x < pitch; x++) {
241
wr[x] = rd[x];
242
}
243
break;
244
245
case 0x01: //Subtract
246
for(int x = 0; x < pitch; x++) {
247
wr[x] = rd[x] + (x - bpp < 0 ? 0 : wr[x - bpp]);
248
}
249
break;
250
251
case 0x02: //Above
252
for(int x = 0; x < pitch; x++) {
253
wr[x] = rd[x] + (y - 1 < 0 ? 0 : wr[x - pitch]);
254
}
255
break;
256
257
case 0x03: //Average
258
for(int x = 0; x < pitch; x++) {
259
short a = x - bpp < 0 ? 0 : wr[x - bpp];
260
short b = y - 1 < 0 ? 0 : wr[x - pitch];
261
262
wr[x] = rd[x] + (uint8_t)((a + b) / 2);
263
}
264
break;
265
266
case 0x04: //Paeth
267
for(int x = 0; x < pitch; x++) {
268
short a = x - bpp < 0 ? 0 : wr[x - bpp];
269
short b = y - 1 < 0 ? 0 : wr[x - pitch];
270
short c = x - bpp < 0 || y - 1 < 0 ? 0 : wr[x - pitch - bpp];
271
272
short p = a + b - c;
273
short pa = p > a ? p - a : a - p;
274
short pb = p > b ? p - b : b - p;
275
short pc = p > c ? p - c : c - p;
276
277
uint8_t paeth = (uint8_t)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c);
278
279
wr[x] = rd[x] + paeth;
280
}
281
break;
282
283
default: //Invalid
284
return false;
285
}
286
287
rd += pitch;
288
wr += pitch;
289
}
290
291
return true;
292
}
293
294
unsigned png::read(const uint8_t *data, unsigned length) {
295
unsigned result = 0;
296
while(length--) result = (result << 8) | (*data++);
297
return result;
298
}
299
300
unsigned png::readbits(const uint8_t *&data) {
301
unsigned result = 0;
302
switch(info.bitDepth) {
303
case 1:
304
result = (*data >> bitpos) & 1;
305
bitpos++;
306
if(bitpos == 8) { data++; bitpos = 0; }
307
break;
308
case 2:
309
result = (*data >> bitpos) & 3;
310
bitpos += 2;
311
if(bitpos == 8) { data++; bitpos = 0; }
312
break;
313
case 4:
314
result = (*data >> bitpos) & 15;
315
bitpos += 4;
316
if(bitpos == 8) { data++; bitpos = 0; }
317
break;
318
case 8:
319
result = *data++;
320
break;
321
case 16:
322
result = (data[0] << 8) | (data[1] << 0);
323
data += 2;
324
break;
325
}
326
return result;
327
}
328
329
png::png() : data(nullptr) {
330
bitpos = 0;
331
}
332
333
png::~png() {
334
if(data) delete[] data;
335
}
336
337
}
338
339
#endif
340
341