Subversion Repositories freemyipod

Rev

Rev 494 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
494 theseven 1
//
2
//
3
//    Copyright 2011 TheSeven
4
//
5
//
6
//    This file is part of emCORE.
7
//
8
//    emCORE is free software: you can redistribute it and/or
9
//    modify it under the terms of the GNU General Public License as
10
//    published by the Free Software Foundation, either version 2 of the
11
//    License, or (at your option) any later version.
12
//
13
//    emCORE is distributed in the hope that it will be useful,
14
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
//    See the GNU General Public License for more details.
17
//
18
//    You should have received a copy of the GNU General Public License along
19
//    with emCORE.  If not, see <http://www.gnu.org/licenses/>.
20
//
21
//
22
 
23
 
24
//#define DEBUG_CONSOLES 2
25
//#define DEBUG_PRINT_SOURCE_LINE
26
 
27
 
28
#include "emcorelib.h"
29
#include "export/libpng.h"
30
#include "tinf.h"
31
#include "png_format.h"
32
 
33
 
34
static uint32_t read32be(const uint8_t* in)
35
{
36
    return (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3];
37
}
38
 
39
struct png_info* png_open(const void* data, size_t size)
40
{
41
    DEBUGF("png_open: Processing file at 0x%08X, size: %d", data, size);
42
    const uint8_t* in = (const uint8_t*)data;
43
    struct png_info info;
44
    memset(&info, 0, sizeof(info));
45
    if (memcmp(in, "\x89PNG\r\n\x1a\n", 8))
46
    {
47
        DEBUGF("png_open: Invalid signature: %02X %02X %02X %02X %02X %02X %02X %02X",
48
               in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]);
49
        return NULL;
50
    }
51
    in += 8;
52
    size -= 8;
53
    bool first = true;
54
    bool ihdr = false;
55
    int idat = 0;
56
    bool iend = false;
57
    while (size)
58
    {
59
        if (iend)
60
        {
61
            DEBUGF("png_open: %d bytes of garbage after IEND chunk", size);
62
            return NULL;
63
        }
64
        uint32_t length = read32be(in);
65
        DEBUGF("png_open: %08X (%d remaining): %c%c%c%c (%08X, %d+12 bytes)",
66
               in, size, in[4], in[5], in[6], in[7], read32be(in + 4), length);
67
        in += 4;
68
        if (length + 12 > size)
69
        {
70
            DEBUGF("png_open: Chunk reaches beyond end of file", size);
71
            return NULL;
72
        }
73
        if (crc32(in, length + 4) != read32be(in + length + 4))
74
        {
75
            DEBUGF("png_open: Bad chunk CRC");
76
            return NULL;
77
        }
78
        uint32_t chunk = read32be(in);
79
        if (chunk & 0x2000)
80
        {
81
            DEBUGF("png_open: Chunk name has reserved bit set: %08X", chunk);
82
            return NULL;
83
        }
84
        if (first && chunk != PNG_CHUNK_IHDR)
85
        {
86
            DEBUGF("png_open: First chunk is not IHDR");
87
            return NULL;
88
        }
89
        if (idat && chunk != PNG_CHUNK_IDAT) idat = 2;
90
        in += 4;
91
        switch (chunk)
92
        {
93
        case PNG_CHUNK_IHDR:
94
            if (length != 13)
95
            {
96
                DEBUGF("png_open: Bad IHDR length: %d bytes", length);
97
                return NULL;
98
            }
99
            if (in[10])
100
            {
101
                DEBUGF("png_open: Unknown compression type 0x%02X", in[10]);
102
                return NULL;
103
            }
104
            if (in[11])
105
            {
106
                DEBUGF("png_open: Unknown filter type 0x%02X", in[11]);
107
                return NULL;
108
            }
109
            switch (in[9])
110
            {
111
            case 0:
112
                if (in[8] == 16) break;
113
            case 3:
114
                if (in[8] == 1 || in[8] == 2 || in[8] == 4 || in[8] == 8)
115
                    break;
116
                DEBUGF("png_open: Invalid depth 0x%02X for color type 0x%02X", in[8], in[9]);
117
                return NULL;
118
            case 2:
119
            case 4:
120
            case 6:
121
                if (in[8] == 8 || in[8] == 16) break;
122
                DEBUGF("png_open: Invalid depth %02X for color type 0x%02X", in[8], in[9]);
123
                return NULL;
124
            default:
125
                DEBUGF("png_open: Unknown color type 0x%02X", in[9]);
126
                return NULL;
127
            }
128
            if (in[12])
129
            {
130
                DEBUGF("png_open: Only non-interlaced files are supported");
131
                return NULL;
132
            }
133
            switch (in[9])
134
            {
135
            case 0:
136
            case 3:
137
                info.channels = 1;
138
                break;
139
            case 4:
140
                info.channels = 2;
141
                break;
142
            case 2:
143
                info.channels = 3;
144
                break;
145
            case 6:
146
                info.channels = 4;
147
                break;
148
            default:
149
                DEBUGF("png_open: Unknown color type 0x%02X", info.colortype);
150
                return NULL;
151
            }
152
            info.width = read32be(in);
153
            info.height = read32be(in + 4);
154
            info.depth = in[8];
155
            info.colortype = in[9];
156
            info.comprtype = in[10];
157
            info.filtertype = in[11];
158
            in += length;
159
            break;
160
        case PNG_CHUNK_IDAT:
161
            if (idat == 2)
162
            {
163
                DEBUGF("png_open: IDAT chunks are not consecutive");
164
                return NULL;
165
            }
166
            if (!idat)
167
            {
168
                info.idat = in - 8;
169
                idat = 1;
170
            }
649 theseven 171
            info.idatlen += length + 12;
494 theseven 172
            in += length;
173
            break;
174
        case PNG_CHUNK_IEND:
175
            if (length)
176
            {
177
                DEBUGF("png_open: IEND chunk has non-zero size");
178
                return NULL;
179
            }
180
            iend = true;
181
            break;
182
        case PNG_CHUNK_PLTE:
183
            if (info.colortype != 3)
184
            {
185
                DEBUGF("png_open: Color type 0x%02X may not have a palette", info.colortype);
186
                return NULL;
187
            }
188
            if (length % 3)
189
            {
190
                DEBUGF("png_open: Palette size is not a multiple of 3");
191
                return NULL;
192
            }
193
            info.palette = (const struct png_rgb*)in;
194
            in += length;
195
            break;
196
        case PNG_CHUNK_tRNS:
197
            switch (info.colortype)
198
            {
199
            case 2:
200
                info.key[0] = (in[0] << 8) | in[1];
201
                info.key[1] = (in[2] << 8) | in[3];
202
                info.key[2] = (in[4] << 8) | in[5];
203
                break;
204
            case 0:
205
                info.key[0] = (in[0] << 8) | in[1];
206
                info.key[1] = (in[0] << 8) | in[1];
207
                info.key[2] = (in[0] << 8) | in[1];
208
                info.keyvalid = 1;
209
                break;
210
            case 3:
211
                info.palalpha = in;
212
                info.palalphacnt = length;
213
                break;
214
            default:
215
                DEBUGF("png_open: Color type 0x%02X may not have a tRNS chunk", info.colortype);
216
                return NULL;
217
            }
218
            in += length;
219
            break;
220
        case PNG_CHUNK_bKGD:
221
            switch (info.colortype)
222
            {
223
            case 2:
224
            case 6:
225
                if (info.depth == 16)
226
                {
227
                    info.bg.r = in[0];
228
                    info.bg.g = in[2];
229
                    info.bg.b = in[4];
230
                }
231
                else
232
                {
233
                    info.bg.r = in[1] << (8 - info.depth);
234
                    info.bg.g = in[3] << (8 - info.depth);
235
                    info.bg.b = in[5] << (8 - info.depth);
236
                }
237
                break;
238
            case 0:
239
            case 4:
240
                if (info.depth == 16)
241
                {
242
                    info.bg.r = in[0];
243
                    info.bg.g = in[0];
244
                    info.bg.b = in[0];
245
                }
246
                else
247
                {
248
                    info.bg.r = in[1] << (8 - info.depth);
249
                    info.bg.g = in[1] << (8 - info.depth);
250
                    info.bg.b = in[1] << (8 - info.depth);
251
                }
252
                break;
253
            case 3:
254
                if (!info.palette)
255
                {
256
                    DEBUGF("png_open: Indexed mode, but no palette before bKGD chunk");
257
                    return NULL;
258
                }
259
                info.bg = info.palette[*in];
260
                break;
261
            default:
262
                DEBUGF("png_open: Unknown color type 0x%02X", info.colortype);
263
                return NULL;
264
            }
265
            in += length;
266
            break;
267
        default:
268
            if (!(chunk & 0x20000000))
269
            {
270
                DEBUGF("png_open: Unknown critical chunk: %08X", chunk);
271
                return NULL;
272
            }
273
            in += length;
274
        }
275
        first = false;
276
        in += 4;
277
        size -= length + 12;
278
    }
279
    if (!iend)
280
    {
281
        DEBUGF("png_open: No IEND chunk");
282
        return NULL;
283
    }
284
    if (info.colortype == 3 && !info.palette)
285
    {
286
        DEBUGF("png_open: Indexed mode, but no palette present");
287
        return NULL;
288
    }
289
    struct png_info* ret = (struct png_info*)malloc(sizeof(info));
290
    if (ret) memcpy(ret, &info, sizeof(info));
291
    else DEBUGF("png_open: Could not allocate memory for png_info struct");
292
    return ret;
293
}
294
 
295
uint32_t png_get_width(struct png_info* info)
296
{
297
    return info->width;
298
}
299
 
300
uint32_t png_get_height(struct png_info* info)
301
{
302
    return info->height;
303
}
304
 
305
void png_set_background(struct png_info* info, uint32_t color)
306
{
307
    info->bg.r = (color >> 0) & 0xff;
308
    info->bg.g = (color >> 8) & 0xff;
309
    info->bg.b = (color >> 16) & 0xff;
310
}
311
 
312
uint8_t* png_decode(struct png_info* info, bool alpha)
313
{
314
    int i, rc;
315
    int filterchannels = info->channels * ((info->depth + 7) / 8);
316
    int scanlinesize = (info->width * info->channels * info->depth + 7) / 8;
317
    int insize = info->height * (1 + scanlinesize);
318
    int unfilteredsize = info->height * scanlinesize;
319
    int outsize = info->height * info->width * (alpha ? 4 : 3);
320
    int bufsize = MAX(insize, outsize);
321
    DEBUGF("png_decode: Input data size: 0x%X", insize);
322
    DEBUGF("png_decode: Unfiltered data size: 0x%X", unfilteredsize);
323
    DEBUGF("png_decode: Output data size: 0x%X", outsize);
324
    DEBUGF("png_decode: Allocating 0x%X bytes of memory for decoding", bufsize);
325
    uint8_t* buf = (uint8_t*)malloc(bufsize);
326
    if (!buf)
327
    {
328
        DEBUGF("png_decode: Could not allocate memory for decompression");
329
        return NULL;
330
    }
331
    uint8_t* in = buf + bufsize - insize;
332
    uint8_t* out = buf;
333
    DEBUGF("png_decode: Allocated decoding buffer at 0x%08X", buf);
334
    DEBUGF("png_decode: Decompressing input data to 0x%08X", in);
335
    if (rc = tinf_zlib_uncompress(in, &insize, info->idat, info->idatlen))
336
    {
337
        DEBUGF("png_decode: Decompression failed: %d", rc);
338
        return NULL;
339
    }
340
    DEBUGF("png_decode: Unfiltering input data to 0x%08X", out);
341
    int row;
342
    for (row = 0; row < info->height; row++)
343
    {
344
        uint8_t filtertype = *in++;
345
        for (i = 0; i < scanlinesize; i++)
346
        {
347
            int predict;
348
            switch (filtertype)
349
            {
350
            case 1:
351
                if (i >= filterchannels) predict = *(out - filterchannels);
352
                else predict = 0;
353
                break;
354
            case 2:
355
                if (row) predict = *(out - scanlinesize);
356
                else predict = 0;
357
                break;
358
            case 3:
359
                predict = 0;
360
                if (i >= filterchannels) predict += *(out - filterchannels);
361
                if (row) predict += *(out - scanlinesize);
362
                predict >>= 1;
363
                break;
364
            case 4:
365
            {
366
                int a, b, c, p, pa, pb, pc;
367
                if (i >= filterchannels) a = *(out - filterchannels);
368
                else a = 0;
369
                if (row > 0) b = *(out - scanlinesize);
370
                else b = 0;
371
                if (row > 0 && i >= filterchannels) c = *(out - scanlinesize - filterchannels);
372
                else c = 0;
373
                p = a + b - c;
374
                pa = ABS(p - a);
375
                pb = ABS(p - b);
376
                pc = ABS(p - c);
377
                if (pa <= pb && pa <= pc) predict = a;
378
                else if (pb <= pc) predict = b;
379
                else predict = c;
380
                break;
381
            }
382
            default:
383
                predict = 0;
384
            }
385
            *out++ = predict + *in++;
386
        }
387
    }
388
    if (!(alpha && info->colortype == 6 && info->depth == 8)
389
     && !(!alpha && info->colortype == 2 && info->depth == 8 && !info->keyvalid))
390
    {
391
        uint8_t* in = buf + bufsize - unfilteredsize;
392
        uint8_t* out = buf;
393
        DEBUGF("png_decode: Moving unfiltered data from 0x%08X to 0x%08X", out, in);
394
        memmove(in, out, unfilteredsize);
395
        DEBUGF("png_decode: Converting unfiltered data to 0x%08X", out);
396
        for (row = 0; row < info->height; row++)
397
        {
398
            int col = 0;
399
            while (col < info->width)
400
            {
401
                int i, a, d;
402
                if (info->depth < 8)
403
                {
404
                    uint8_t byte = *buf++;
405
                    for (i = 0; i < 8 && col < info->width; i += info->depth)
406
                    {
407
                        if (info->colortype == 3)
408
                        {
409
                            int index = byte >> (8 - info->depth);
410
                            struct png_rgb color = info->palette[index];
411
                            if (index < info->palalphacnt) a = info->palalpha[index];
412
                            else a = 255;
413
                            if (!alpha && a != 255)
414
                            {
415
                                *out++ = (color.r * a + info->bg.r * (255 - a)) >> 8;
416
                                *out++ = (color.g * a + info->bg.g * (255 - a)) >> 8;
417
                                *out++ = (color.b * a + info->bg.b * (255 - a)) >> 8;
418
                            }
419
                            else
420
                            {
421
                                *out++ = color.r;
422
                                *out++ = color.g;
423
                                *out++ = color.b;
424
                                if (alpha) *out++ = a;
425
                            }
426
                        }
427
                        else
428
                        {
429
                            int color = byte >> (8 - info->depth);
430
                            for (d = info->depth; d < 8; d <<= 1)
431
                                color |= color << d;
432
                            if (info->keyvalid && color == info->key[0]) a = 0;
433
                            else a = 255;
434
                            if (!alpha && !a)
435
                            {
436
                                *out++ = info->bg.r;
437
                                *out++ = info->bg.r;
438
                                *out++ = info->bg.r;
439
                            }
440
                            else
441
                            {
442
                                *out++ = color;
443
                                *out++ = color;
444
                                *out++ = color;
445
                                if (alpha) *out++ = a;
446
                            }
447
                        }
448
                        byte <<= info->depth;
449
                        col++;
450
                    }
451
                }
452
                else
453
                {
454
                    if (info->colortype == 3)
455
                    {
456
                        int index = *in++;
457
                        struct png_rgb color = info->palette[index];
458
                        if (index < info->palalphacnt) a = info->palalpha[index];
459
                        else a = 255;
460
                        if (!alpha && a != 255)
461
                        {
462
                            *out++ = (color.r * a + info->bg.r * (255 - a)) >> 8;
463
                            *out++ = (color.g * a + info->bg.g * (255 - a)) >> 8;
464
                            *out++ = (color.b * a + info->bg.b * (255 - a)) >> 8;
465
                        }
466
                        else
467
                        {
468
                            *out++ = color.r;
469
                            *out++ = color.g;
470
                            *out++ = color.b;
471
                            if (alpha) *out++ = a;
472
                        }
473
                    }
474
                    else
475
                    {
476
                        int chan[4];
477
                        for (i = 0; i < info->channels; i++)
478
                        {
479
                            chan[i] = *in++;
480
                            if (info->depth == 16) chan[i] = (chan[i] << 8) | *in++;
481
                        }
482
                        if (!(info->colortype & 2))
483
                        {
484
                            chan[3] = chan[1];
485
                            chan[2] = chan[0];
486
                            chan[1] = chan[0];
487
                        }
488
                        if (!(info->colortype & 4))
489
                        {
490
                            if (info->keyvalid && chan[0] == info->key[0]
491
                             && chan[1] == info->key[1] && chan[2] == info->key[2]) a = 0;
492
                            else a = 255;
493
                        }
494
                        else if (info->depth == 16) a = chan[3] >> 8;
495
                        else a = chan[3];
496
                        if (info->depth == 16)
497
                        {
498
                            chan[0] >>= 8;
499
                            chan[1] >>= 8;
500
                            chan[2] >>= 8;
501
                        }
502
                        if (!alpha && a != 255)
503
                        {
504
                            *out++ = (chan[0] * a + info->bg.r * (255 - a)) >> 8;
505
                            *out++ = (chan[1] * a + info->bg.g * (255 - a)) >> 8;
506
                            *out++ = (chan[2] * a + info->bg.b * (255 - a)) >> 8;
507
                        }
508
                        else
509
                        {
510
                            *out++ = chan[0];
511
                            *out++ = chan[1];
512
                            *out++ = chan[2];
513
                            if (alpha) *out++ = a;
514
                        }
515
                    }
516
                    col++;
517
                }
518
            }
519
        }
520
    }
521
    if (outsize != bufsize)
522
    {
523
        DEBUGF("png_decode: Shrinking buffer to 0x%X bytes", outsize);
524
        out = (uint8_t*)realloc(buf, outsize);
525
        if (!out)
526
        {
527
            free(buf);
528
            DEBUGF("png_decode: Failed to trim output buffer");
529
        }
530
        DEBUGF("png_decode: Buffer is now at 0x%08X", out);
531
        return out;
532
    }
533
    return buf;
534
}
535
 
536
struct png_rgba* png_decode_rgba(struct png_info* info)
537
{
538
    return (struct png_rgba*)png_decode(info, true);
539
}
540
 
541
struct png_rgb* png_decode_rgb(struct png_info* info)
542
{
543
    return (struct png_rgb*)png_decode(info, false);
544
}
545
 
546
void png_destroy(struct png_info* info)
547
{
548
    free(info);
549
}