Statistics
| Branch: | Revision:

ffmpeg / libavcodec / png.c @ 5509bffa

History | View | Annotate | Download (28.3 KB)

1 4eff7cf4 Michael Niedermayer
/*
2
 * PNG image format
3
 * Copyright (c) 2003 Fabrice Bellard.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17 5509bffa Diego Biurrun
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 4eff7cf4 Michael Niedermayer
 */
19
#include "avcodec.h"
20
21
/* TODO:
22
 * - add 2, 4 and 16 bit depth support
23
 * - use filters when generating a png (better compression)
24
 */
25
26
#ifdef CONFIG_ZLIB
27
#include <zlib.h>
28
29
//#define DEBUG
30
31
#define PNG_COLOR_MASK_PALETTE    1
32
#define PNG_COLOR_MASK_COLOR      2
33
#define PNG_COLOR_MASK_ALPHA      4
34
35
#define PNG_COLOR_TYPE_GRAY 0
36
#define PNG_COLOR_TYPE_PALETTE  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
37
#define PNG_COLOR_TYPE_RGB        (PNG_COLOR_MASK_COLOR)
38
#define PNG_COLOR_TYPE_RGB_ALPHA  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
39
#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
40
41
#define PNG_FILTER_VALUE_NONE  0
42
#define PNG_FILTER_VALUE_SUB   1
43
#define PNG_FILTER_VALUE_UP    2
44
#define PNG_FILTER_VALUE_AVG   3
45
#define PNG_FILTER_VALUE_PAETH 4
46
47
#define PNG_IHDR      0x0001
48
#define PNG_IDAT      0x0002
49
#define PNG_ALLIMAGE  0x0004
50
#define PNG_PLTE      0x0008
51
52
#define NB_PASSES 7
53
54
#define IOBUF_SIZE 4096
55
56
typedef struct PNGContext {
57
    uint8_t *bytestream;
58
    uint8_t *bytestream_start;
59
    uint8_t *bytestream_end;
60
    AVFrame picture;
61
62
    int state;
63
    int width, height;
64
    int bit_depth;
65
    int color_type;
66
    int compression_type;
67
    int interlace_type;
68
    int filter_type;
69
    int channels;
70
    int bits_per_pixel;
71
    int bpp;
72 115329f1 Diego Biurrun
73 4eff7cf4 Michael Niedermayer
    uint8_t *image_buf;
74
    int image_linesize;
75
    uint32_t palette[256];
76
    uint8_t *crow_buf;
77
    uint8_t *last_row;
78
    uint8_t *tmp_row;
79
    int pass;
80
    int crow_size; /* compressed row size (include filter type) */
81
    int row_size; /* decompressed row size */
82
    int pass_row_size; /* decompress row size of the current pass */
83
    int y;
84
    z_stream zstream;
85
    uint8_t buf[IOBUF_SIZE];
86
} PNGContext;
87
88
static unsigned int get32(uint8_t **b){
89
    (*b) += 4;
90
    return ((*b)[-4]<<24) + ((*b)[-3]<<16) + ((*b)[-2]<<8) + (*b)[-1];
91
}
92
93
static void put32(uint8_t **b, unsigned int v){
94
    *(*b)++= v>>24;
95
    *(*b)++= v>>16;
96
    *(*b)++= v>>8;
97
    *(*b)++= v;
98
}
99
100
static const uint8_t pngsig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
101
102
/* Mask to determine which y pixels are valid in a pass */
103
static const uint8_t png_pass_ymask[NB_PASSES] = {
104
    0x80, 0x80, 0x08, 0x88, 0x22, 0xaa, 0x55,
105
};
106
107
/* Mask to determine which y pixels can be written in a pass */
108
static const uint8_t png_pass_dsp_ymask[NB_PASSES] = {
109
    0xff, 0xff, 0x0f, 0xcc, 0x33, 0xff, 0x55,
110
};
111
112
/* minimum x value */
113
static const uint8_t png_pass_xmin[NB_PASSES] = {
114
    0, 4, 0, 2, 0, 1, 0
115
};
116
117
/* x shift to get row width */
118
static const uint8_t png_pass_xshift[NB_PASSES] = {
119
    3, 3, 2, 2, 1, 1, 0
120
};
121
122
/* Mask to determine which pixels are valid in a pass */
123
static const uint8_t png_pass_mask[NB_PASSES] = {
124
    0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff
125
};
126
127
/* Mask to determine which pixels to overwrite while displaying */
128 115329f1 Diego Biurrun
static const uint8_t png_pass_dsp_mask[NB_PASSES] = {
129 4eff7cf4 Michael Niedermayer
    0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff
130
};
131
#if 0
132
static int png_probe(AVProbeData *pd)
133
{
134
    if (pd->buf_size >= 8 &&
135
        memcmp(pd->buf, pngsig, 8) == 0)
136
        return AVPROBE_SCORE_MAX;
137
    else
138
        return 0;
139
}
140
#endif
141
static void *png_zalloc(void *opaque, unsigned int items, unsigned int size)
142
{
143 0ecca7a4 Michael Niedermayer
    if(items >= UINT_MAX / size)
144
        return NULL;
145 4eff7cf4 Michael Niedermayer
    return av_malloc(items * size);
146
}
147
148
static void png_zfree(void *opaque, void *ptr)
149
{
150
    av_free(ptr);
151
}
152
153
static int png_get_nb_channels(int color_type)
154
{
155
    int channels;
156
    channels = 1;
157
    if ((color_type & (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)) ==
158
        PNG_COLOR_MASK_COLOR)
159
        channels = 3;
160
    if (color_type & PNG_COLOR_MASK_ALPHA)
161
        channels++;
162
    return channels;
163
}
164
165
/* compute the row size of an interleaved pass */
166
static int png_pass_row_size(int pass, int bits_per_pixel, int width)
167
{
168
    int shift, xmin, pass_width;
169
170
    xmin = png_pass_xmin[pass];
171
    if (width <= xmin)
172
        return 0;
173
    shift = png_pass_xshift[pass];
174
    pass_width = (width - xmin + (1 << shift) - 1) >> shift;
175
    return (pass_width * bits_per_pixel + 7) >> 3;
176
}
177
178
/* NOTE: we try to construct a good looking image at each pass. width
179
   is the original image width. We also do pixel format convertion at
180
   this stage */
181 115329f1 Diego Biurrun
static void png_put_interlaced_row(uint8_t *dst, int width,
182
                                   int bits_per_pixel, int pass,
183 4eff7cf4 Michael Niedermayer
                                   int color_type, const uint8_t *src)
184
{
185
    int x, mask, dsp_mask, j, src_x, b, bpp;
186
    uint8_t *d;
187
    const uint8_t *s;
188 115329f1 Diego Biurrun
189 4eff7cf4 Michael Niedermayer
    mask = png_pass_mask[pass];
190
    dsp_mask = png_pass_dsp_mask[pass];
191
    switch(bits_per_pixel) {
192
    case 1:
193
        /* we must intialize the line to zero before writing to it */
194
        if (pass == 0)
195
            memset(dst, 0, (width + 7) >> 3);
196
        src_x = 0;
197
        for(x = 0; x < width; x++) {
198
            j = (x & 7);
199
            if ((dsp_mask << j) & 0x80) {
200
                b = (src[src_x >> 3] >> (7 - (src_x & 7))) & 1;
201
                dst[x >> 3] |= b << (7 - j);
202
            }
203
            if ((mask << j) & 0x80)
204
                src_x++;
205
        }
206
        break;
207
    default:
208
        bpp = bits_per_pixel >> 3;
209
        d = dst;
210
        s = src;
211
        if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
212
            for(x = 0; x < width; x++) {
213
                j = x & 7;
214
                if ((dsp_mask << j) & 0x80) {
215
                    *(uint32_t *)d = (s[3] << 24) | (s[0] << 16) | (s[1] << 8) | s[2];
216
                }
217
                d += bpp;
218
                if ((mask << j) & 0x80)
219
                    s += bpp;
220
            }
221
        } else {
222
            for(x = 0; x < width; x++) {
223
                j = x & 7;
224
                if ((dsp_mask << j) & 0x80) {
225
                    memcpy(d, s, bpp);
226
                }
227
                d += bpp;
228
                if ((mask << j) & 0x80)
229
                    s += bpp;
230
            }
231
        }
232
        break;
233
    }
234
}
235
236 115329f1 Diego Biurrun
static void png_get_interlaced_row(uint8_t *dst, int row_size,
237
                                   int bits_per_pixel, int pass,
238 4eff7cf4 Michael Niedermayer
                                   const uint8_t *src, int width)
239
{
240
    int x, mask, dst_x, j, b, bpp;
241
    uint8_t *d;
242
    const uint8_t *s;
243
244
    mask = png_pass_mask[pass];
245
    switch(bits_per_pixel) {
246
    case 1:
247
        memset(dst, 0, row_size);
248
        dst_x = 0;
249
        for(x = 0; x < width; x++) {
250
            j = (x & 7);
251
            if ((mask << j) & 0x80) {
252
                b = (src[x >> 3] >> (7 - j)) & 1;
253
                dst[dst_x >> 3] |= b << (7 - (dst_x & 7));
254
                dst_x++;
255
            }
256
        }
257
        break;
258
    default:
259
        bpp = bits_per_pixel >> 3;
260
        d = dst;
261
        s = src;
262
        for(x = 0; x < width; x++) {
263
            j = x & 7;
264
            if ((mask << j) & 0x80) {
265
                memcpy(d, s, bpp);
266
                d += bpp;
267
            }
268
            s += bpp;
269
        }
270
        break;
271
    }
272
}
273
274
/* XXX: optimize */
275
/* NOTE: 'dst' can be equal to 'last' */
276 115329f1 Diego Biurrun
static void png_filter_row(uint8_t *dst, int filter_type,
277 4eff7cf4 Michael Niedermayer
                           uint8_t *src, uint8_t *last, int size, int bpp)
278
{
279
    int i, p;
280
281
    switch(filter_type) {
282
    case PNG_FILTER_VALUE_NONE:
283
        memcpy(dst, src, size);
284
        break;
285
    case PNG_FILTER_VALUE_SUB:
286
        for(i = 0; i < bpp; i++) {
287
            dst[i] = src[i];
288
        }
289
        for(i = bpp; i < size; i++) {
290
            p = dst[i - bpp];
291
            dst[i] = p + src[i];
292
        }
293
        break;
294
    case PNG_FILTER_VALUE_UP:
295
        for(i = 0; i < size; i++) {
296
            p = last[i];
297
            dst[i] = p + src[i];
298
        }
299
        break;
300
    case PNG_FILTER_VALUE_AVG:
301
        for(i = 0; i < bpp; i++) {
302
            p = (last[i] >> 1);
303
            dst[i] = p + src[i];
304
        }
305
        for(i = bpp; i < size; i++) {
306
            p = ((dst[i - bpp] + last[i]) >> 1);
307
            dst[i] = p + src[i];
308
        }
309
        break;
310
    case PNG_FILTER_VALUE_PAETH:
311
        for(i = 0; i < bpp; i++) {
312
            p = last[i];
313
            dst[i] = p + src[i];
314
        }
315
        for(i = bpp; i < size; i++) {
316
            int a, b, c, pa, pb, pc;
317
318
            a = dst[i - bpp];
319
            b = last[i];
320
            c = last[i - bpp];
321
322
            p = b - c;
323
            pc = a - c;
324
325
            pa = abs(p);
326
            pb = abs(pc);
327
            pc = abs(p + pc);
328
329
            if (pa <= pb && pa <= pc)
330
                p = a;
331
            else if (pb <= pc)
332
                p = b;
333
            else
334
                p = c;
335
            dst[i] = p + src[i];
336
        }
337
        break;
338
    }
339
}
340
341
static void convert_from_rgba32(uint8_t *dst, const uint8_t *src, int width)
342
{
343
    uint8_t *d;
344
    int j;
345
    unsigned int v;
346 115329f1 Diego Biurrun
347 4eff7cf4 Michael Niedermayer
    d = dst;
348
    for(j = 0; j < width; j++) {
349
        v = ((uint32_t *)src)[j];
350
        d[0] = v >> 16;
351
        d[1] = v >> 8;
352
        d[2] = v;
353
        d[3] = v >> 24;
354
        d += 4;
355
    }
356
}
357
358
static void convert_to_rgba32(uint8_t *dst, const uint8_t *src, int width)
359
{
360
    int j;
361
    unsigned int r, g, b, a;
362
363
    for(j = 0;j < width; j++) {
364
        r = src[0];
365
        g = src[1];
366
        b = src[2];
367
        a = src[3];
368
        *(uint32_t *)dst = (a << 24) | (r << 16) | (g << 8) | b;
369
        dst += 4;
370
        src += 4;
371
    }
372
}
373
374
/* process exactly one decompressed row */
375
static void png_handle_row(PNGContext *s)
376
{
377
    uint8_t *ptr, *last_row;
378
    int got_line;
379 115329f1 Diego Biurrun
380 4eff7cf4 Michael Niedermayer
    if (!s->interlace_type) {
381
        ptr = s->image_buf + s->image_linesize * s->y;
382
        /* need to swap bytes correctly for RGB_ALPHA */
383
        if (s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
384 115329f1 Diego Biurrun
            png_filter_row(s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
385 4eff7cf4 Michael Niedermayer
                           s->last_row, s->row_size, s->bpp);
386
            memcpy(s->last_row, s->tmp_row, s->row_size);
387
            convert_to_rgba32(ptr, s->tmp_row, s->width);
388
        } else {
389
            /* in normal case, we avoid one copy */
390
            if (s->y == 0)
391
                last_row = s->last_row;
392
            else
393
                last_row = ptr - s->image_linesize;
394 115329f1 Diego Biurrun
395
            png_filter_row(ptr, s->crow_buf[0], s->crow_buf + 1,
396 4eff7cf4 Michael Niedermayer
                           last_row, s->row_size, s->bpp);
397
        }
398
        s->y++;
399
        if (s->y == s->height) {
400
            s->state |= PNG_ALLIMAGE;
401
        }
402
    } else {
403
        got_line = 0;
404
        for(;;) {
405
            ptr = s->image_buf + s->image_linesize * s->y;
406
            if ((png_pass_ymask[s->pass] << (s->y & 7)) & 0x80) {
407
                /* if we already read one row, it is time to stop to
408
                   wait for the next one */
409
                if (got_line)
410
                    break;
411 115329f1 Diego Biurrun
                png_filter_row(s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
412 4eff7cf4 Michael Niedermayer
                               s->last_row, s->pass_row_size, s->bpp);
413
                memcpy(s->last_row, s->tmp_row, s->pass_row_size);
414
                got_line = 1;
415
            }
416
            if ((png_pass_dsp_ymask[s->pass] << (s->y & 7)) & 0x80) {
417
                /* NOTE: rgba32 is handled directly in png_put_interlaced_row */
418 115329f1 Diego Biurrun
                png_put_interlaced_row(ptr, s->width, s->bits_per_pixel, s->pass,
419 4eff7cf4 Michael Niedermayer
                                       s->color_type, s->last_row);
420
            }
421
            s->y++;
422
            if (s->y == s->height) {
423
                for(;;) {
424
                    if (s->pass == NB_PASSES - 1) {
425
                        s->state |= PNG_ALLIMAGE;
426
                        goto the_end;
427
                    } else {
428
                        s->pass++;
429
                        s->y = 0;
430 115329f1 Diego Biurrun
                        s->pass_row_size = png_pass_row_size(s->pass,
431
                                                             s->bits_per_pixel,
432 4eff7cf4 Michael Niedermayer
                                                             s->width);
433
                        s->crow_size = s->pass_row_size + 1;
434
                        if (s->pass_row_size != 0)
435
                            break;
436
                        /* skip pass if empty row */
437
                    }
438
                }
439
            }
440
        }
441
    the_end: ;
442
    }
443
}
444
445
static int png_decode_idat(PNGContext *s, int length)
446
{
447
    int ret;
448
    s->zstream.avail_in = length;
449
    s->zstream.next_in = s->bytestream;
450
    s->bytestream += length;
451 115329f1 Diego Biurrun
452 4eff7cf4 Michael Niedermayer
    if(s->bytestream > s->bytestream_end)
453
        return -1;
454
455
    /* decode one line if possible */
456
    while (s->zstream.avail_in > 0) {
457
        ret = inflate(&s->zstream, Z_PARTIAL_FLUSH);
458
        if (ret != Z_OK && ret != Z_STREAM_END) {
459
            return -1;
460
        }
461
        if (s->zstream.avail_out == 0) {
462
            if (!(s->state & PNG_ALLIMAGE)) {
463
                png_handle_row(s);
464
            }
465
            s->zstream.avail_out = s->crow_size;
466
            s->zstream.next_out = s->crow_buf;
467
        }
468
    }
469
    return 0;
470
}
471
472 115329f1 Diego Biurrun
static int decode_frame(AVCodecContext *avctx,
473 4eff7cf4 Michael Niedermayer
                        void *data, int *data_size,
474
                        uint8_t *buf, int buf_size)
475
{
476
    PNGContext * const s = avctx->priv_data;
477
    AVFrame *picture = data;
478
    AVFrame * const p= (AVFrame*)&s->picture;
479
    uint32_t tag, length;
480
    int ret, crc;
481
482
    s->bytestream_start=
483
    s->bytestream= buf;
484
    s->bytestream_end= buf + buf_size;
485
486
    /* check signature */
487
    if (memcmp(s->bytestream, pngsig, 8) != 0)
488
        return -1;
489
    s->bytestream+= 8;
490
    s->y=
491
    s->state=0;
492
//    memset(s, 0, sizeof(PNGContext));
493
    /* init the zlib */
494
    s->zstream.zalloc = png_zalloc;
495
    s->zstream.zfree = png_zfree;
496
    s->zstream.opaque = NULL;
497
    ret = inflateInit(&s->zstream);
498
    if (ret != Z_OK)
499
        return -1;
500
    for(;;) {
501 e20815b3 Milan Cutka
        int tag32;
502 4eff7cf4 Michael Niedermayer
        if (s->bytestream >= s->bytestream_end)
503
            goto fail;
504
        length = get32(&s->bytestream);
505
        if (length > 0x7fffffff)
506
            goto fail;
507 e20815b3 Milan Cutka
        tag32 = get32(&s->bytestream);
508
        tag = bswap_32(tag32);
509 4eff7cf4 Michael Niedermayer
#ifdef DEBUG
510 115329f1 Diego Biurrun
        printf("png: tag=%c%c%c%c length=%u\n",
511 4eff7cf4 Michael Niedermayer
               (tag & 0xff),
512
               ((tag >> 8) & 0xff),
513
               ((tag >> 16) & 0xff),
514
               ((tag >> 24) & 0xff), length);
515
#endif
516
        switch(tag) {
517
        case MKTAG('I', 'H', 'D', 'R'):
518
            if (length != 13)
519
                goto fail;
520
            s->width = get32(&s->bytestream);
521
            s->height = get32(&s->bytestream);
522 0ecca7a4 Michael Niedermayer
            if(avcodec_check_dimensions(avctx, s->width, s->height)){
523
                s->width= s->height= 0;
524
                goto fail;
525
            }
526 4eff7cf4 Michael Niedermayer
            s->bit_depth = *s->bytestream++;
527
            s->color_type = *s->bytestream++;
528
            s->compression_type = *s->bytestream++;
529
            s->filter_type = *s->bytestream++;
530
            s->interlace_type = *s->bytestream++;
531
            crc = get32(&s->bytestream);
532
            s->state |= PNG_IHDR;
533
#ifdef DEBUG
534 115329f1 Diego Biurrun
            printf("width=%d height=%d depth=%d color_type=%d compression_type=%d filter_type=%d interlace_type=%d\n",
535
                   s->width, s->height, s->bit_depth, s->color_type,
536 4eff7cf4 Michael Niedermayer
                   s->compression_type, s->filter_type, s->interlace_type);
537
#endif
538
            break;
539
        case MKTAG('I', 'D', 'A', 'T'):
540
            if (!(s->state & PNG_IHDR))
541
                goto fail;
542
            if (!(s->state & PNG_IDAT)) {
543
                /* init image info */
544
                avctx->width = s->width;
545
                avctx->height = s->height;
546
547
                s->channels = png_get_nb_channels(s->color_type);
548
                s->bits_per_pixel = s->bit_depth * s->channels;
549
                s->bpp = (s->bits_per_pixel + 7) >> 3;
550
                s->row_size = (avctx->width * s->bits_per_pixel + 7) >> 3;
551
552 115329f1 Diego Biurrun
                if (s->bit_depth == 8 &&
553 4eff7cf4 Michael Niedermayer
                    s->color_type == PNG_COLOR_TYPE_RGB) {
554
                    avctx->pix_fmt = PIX_FMT_RGB24;
555 115329f1 Diego Biurrun
                } else if (s->bit_depth == 8 &&
556 4eff7cf4 Michael Niedermayer
                           s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
557
                    avctx->pix_fmt = PIX_FMT_RGBA32;
558 115329f1 Diego Biurrun
                } else if (s->bit_depth == 8 &&
559 4eff7cf4 Michael Niedermayer
                           s->color_type == PNG_COLOR_TYPE_GRAY) {
560
                    avctx->pix_fmt = PIX_FMT_GRAY8;
561 115329f1 Diego Biurrun
                } else if (s->bit_depth == 1 &&
562 4eff7cf4 Michael Niedermayer
                           s->color_type == PNG_COLOR_TYPE_GRAY) {
563
                    avctx->pix_fmt = PIX_FMT_MONOBLACK;
564
                } else if (s->color_type == PNG_COLOR_TYPE_PALETTE) {
565
                    avctx->pix_fmt = PIX_FMT_PAL8;
566
                } else {
567
                    goto fail;
568
                }
569
                if(p->data[0])
570
                    avctx->release_buffer(avctx, p);
571 115329f1 Diego Biurrun
572 4eff7cf4 Michael Niedermayer
                p->reference= 0;
573
                if(avctx->get_buffer(avctx, p) < 0){
574
                    av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
575
                    goto fail;
576
                }
577
                p->pict_type= FF_I_TYPE;
578
                p->key_frame= 1;
579
                p->interlaced_frame = !!s->interlace_type;
580
581
                /* compute the compressed row size */
582
                if (!s->interlace_type) {
583
                    s->crow_size = s->row_size + 1;
584
                } else {
585
                    s->pass = 0;
586 115329f1 Diego Biurrun
                    s->pass_row_size = png_pass_row_size(s->pass,
587
                                                         s->bits_per_pixel,
588 4eff7cf4 Michael Niedermayer
                                                         s->width);
589
                    s->crow_size = s->pass_row_size + 1;
590
                }
591
#ifdef DEBUG
592 115329f1 Diego Biurrun
                printf("row_size=%d crow_size =%d\n",
593 4eff7cf4 Michael Niedermayer
                       s->row_size, s->crow_size);
594
#endif
595
                s->image_buf = p->data[0];
596
                s->image_linesize = p->linesize[0];
597
                /* copy the palette if needed */
598
                if (s->color_type == PNG_COLOR_TYPE_PALETTE)
599
                    memcpy(p->data[1], s->palette, 256 * sizeof(uint32_t));
600
                /* empty row is used if differencing to the first row */
601
                s->last_row = av_mallocz(s->row_size);
602
                if (!s->last_row)
603
                    goto fail;
604
                if (s->interlace_type ||
605
                    s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
606
                    s->tmp_row = av_malloc(s->row_size);
607
                    if (!s->tmp_row)
608
                        goto fail;
609
                }
610
                /* compressed row */
611
                s->crow_buf = av_malloc(s->row_size + 1);
612
                if (!s->crow_buf)
613
                    goto fail;
614
                s->zstream.avail_out = s->crow_size;
615
                s->zstream.next_out = s->crow_buf;
616
            }
617
            s->state |= PNG_IDAT;
618
            if (png_decode_idat(s, length) < 0)
619
                goto fail;
620
            /* skip crc */
621
            crc = get32(&s->bytestream);
622
            break;
623
        case MKTAG('P', 'L', 'T', 'E'):
624
            {
625
                int n, i, r, g, b;
626 115329f1 Diego Biurrun
627 4eff7cf4 Michael Niedermayer
                if ((length % 3) != 0 || length > 256 * 3)
628
                    goto skip_tag;
629
                /* read the palette */
630
                n = length / 3;
631
                for(i=0;i<n;i++) {
632
                    r = *s->bytestream++;
633
                    g = *s->bytestream++;
634
                    b = *s->bytestream++;
635
                    s->palette[i] = (0xff << 24) | (r << 16) | (g << 8) | b;
636
                }
637
                for(;i<256;i++) {
638
                    s->palette[i] = (0xff << 24);
639
                }
640
                s->state |= PNG_PLTE;
641
                crc = get32(&s->bytestream);
642
            }
643
            break;
644
        case MKTAG('t', 'R', 'N', 'S'):
645
            {
646
                int v, i;
647
648
                /* read the transparency. XXX: Only palette mode supported */
649
                if (s->color_type != PNG_COLOR_TYPE_PALETTE ||
650
                    length > 256 ||
651
                    !(s->state & PNG_PLTE))
652
                    goto skip_tag;
653
                for(i=0;i<length;i++) {
654
                    v = *s->bytestream++;
655
                    s->palette[i] = (s->palette[i] & 0x00ffffff) | (v << 24);
656
                }
657
                crc = get32(&s->bytestream);
658
            }
659
            break;
660
        case MKTAG('I', 'E', 'N', 'D'):
661
            if (!(s->state & PNG_ALLIMAGE))
662
                goto fail;
663
            crc = get32(&s->bytestream);
664
            goto exit_loop;
665
        default:
666
            /* skip tag */
667
        skip_tag:
668
            s->bytestream += length + 4;
669
            break;
670
        }
671
    }
672
 exit_loop:
673
    *picture= *(AVFrame*)&s->picture;
674
    *data_size = sizeof(AVPicture);
675
676
    ret = s->bytestream - s->bytestream_start;
677
 the_end:
678
    inflateEnd(&s->zstream);
679
    av_freep(&s->crow_buf);
680
    av_freep(&s->last_row);
681
    av_freep(&s->tmp_row);
682
    return ret;
683
 fail:
684
    ret = -1;
685
    goto the_end;
686
}
687
688
static void png_write_chunk(uint8_t **f, uint32_t tag,
689
                            const uint8_t *buf, int length)
690
{
691
    uint32_t crc;
692
    uint8_t tagbuf[4];
693
694
    put32(f, length);
695
    crc = crc32(0, Z_NULL, 0);
696
    tagbuf[0] = tag;
697
    tagbuf[1] = tag >> 8;
698
    tagbuf[2] = tag >> 16;
699
    tagbuf[3] = tag >> 24;
700
    crc = crc32(crc, tagbuf, 4);
701
    put32(f, bswap_32(tag));
702
    if (length > 0) {
703
        crc = crc32(crc, buf, length);
704
        memcpy(*f, buf, length);
705
        *f += length;
706
    }
707
    put32(f, crc);
708
}
709
710
/* XXX: use avcodec generic function ? */
711
static void to_be32(uint8_t *p, uint32_t v)
712
{
713
    p[0] = v >> 24;
714
    p[1] = v >> 16;
715
    p[2] = v >> 8;
716
    p[3] = v;
717
}
718
719
/* XXX: do filtering */
720
static int png_write_row(PNGContext *s, const uint8_t *data, int size)
721
{
722
    int ret;
723
724
    s->zstream.avail_in = size;
725
    s->zstream.next_in = (uint8_t *)data;
726
    while (s->zstream.avail_in > 0) {
727
        ret = deflate(&s->zstream, Z_NO_FLUSH);
728
        if (ret != Z_OK)
729
            return -1;
730
        if (s->zstream.avail_out == 0) {
731 0ecca7a4 Michael Niedermayer
            if(s->bytestream_end - s->bytestream > IOBUF_SIZE + 100)
732
                png_write_chunk(&s->bytestream, MKTAG('I', 'D', 'A', 'T'), s->buf, IOBUF_SIZE);
733 4eff7cf4 Michael Niedermayer
            s->zstream.avail_out = IOBUF_SIZE;
734
            s->zstream.next_out = s->buf;
735
        }
736
    }
737
    return 0;
738
}
739
740
static int common_init(AVCodecContext *avctx){
741
    PNGContext *s = avctx->priv_data;
742
743
    avcodec_get_frame_defaults((AVFrame*)&s->picture);
744
    avctx->coded_frame= (AVFrame*)&s->picture;
745
//    s->avctx= avctx;
746
747
    return 0;
748
}
749
750
static int encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data){
751
    PNGContext *s = avctx->priv_data;
752
    AVFrame *pict = data;
753
    AVFrame * const p= (AVFrame*)&s->picture;
754
    int bit_depth, color_type, y, len, row_size, ret, is_progressive;
755
    int bits_per_pixel, pass_row_size;
756
    uint8_t *ptr;
757
    uint8_t *crow_buf = NULL;
758
    uint8_t *tmp_buf = NULL;
759
760
    *p = *pict;
761
    p->pict_type= FF_I_TYPE;
762
    p->key_frame= 1;
763 115329f1 Diego Biurrun
764 4eff7cf4 Michael Niedermayer
    s->bytestream_start=
765
    s->bytestream= buf;
766
    s->bytestream_end= buf+buf_size;
767
768
    is_progressive = !!(avctx->flags & CODEC_FLAG_INTERLACED_DCT);
769
    switch(avctx->pix_fmt) {
770
    case PIX_FMT_RGBA32:
771
        bit_depth = 8;
772
        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
773
        break;
774
    case PIX_FMT_RGB24:
775
        bit_depth = 8;
776
        color_type = PNG_COLOR_TYPE_RGB;
777
        break;
778
    case PIX_FMT_GRAY8:
779
        bit_depth = 8;
780
        color_type = PNG_COLOR_TYPE_GRAY;
781
        break;
782
    case PIX_FMT_MONOBLACK:
783
        bit_depth = 1;
784
        color_type = PNG_COLOR_TYPE_GRAY;
785
        break;
786
    case PIX_FMT_PAL8:
787
        bit_depth = 8;
788
        color_type = PNG_COLOR_TYPE_PALETTE;
789
        break;
790
    default:
791
        return -1;
792
    }
793
    bits_per_pixel = png_get_nb_channels(color_type) * bit_depth;
794
    row_size = (avctx->width * bits_per_pixel + 7) >> 3;
795
796
    s->zstream.zalloc = png_zalloc;
797
    s->zstream.zfree = png_zfree;
798
    s->zstream.opaque = NULL;
799
    ret = deflateInit2(&s->zstream, Z_DEFAULT_COMPRESSION,
800
                       Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY);
801
    if (ret != Z_OK)
802
        return -1;
803
    crow_buf = av_malloc(row_size + 1);
804
    if (!crow_buf)
805
        goto fail;
806
    if (is_progressive) {
807
        tmp_buf = av_malloc(row_size + 1);
808
        if (!tmp_buf)
809
            goto fail;
810
    }
811
812
    /* write png header */
813
    memcpy(s->bytestream, pngsig, 8);
814
    s->bytestream += 8;
815 115329f1 Diego Biurrun
816 4eff7cf4 Michael Niedermayer
    to_be32(s->buf, avctx->width);
817
    to_be32(s->buf + 4, avctx->height);
818
    s->buf[8] = bit_depth;
819
    s->buf[9] = color_type;
820
    s->buf[10] = 0; /* compression type */
821
    s->buf[11] = 0; /* filter type */
822
    s->buf[12] = is_progressive; /* interlace type */
823 115329f1 Diego Biurrun
824 4eff7cf4 Michael Niedermayer
    png_write_chunk(&s->bytestream, MKTAG('I', 'H', 'D', 'R'), s->buf, 13);
825
826
    /* put the palette if needed */
827
    if (color_type == PNG_COLOR_TYPE_PALETTE) {
828
        int has_alpha, alpha, i;
829
        unsigned int v;
830
        uint32_t *palette;
831
        uint8_t *alpha_ptr;
832 115329f1 Diego Biurrun
833 4eff7cf4 Michael Niedermayer
        palette = (uint32_t *)p->data[1];
834
        ptr = s->buf;
835
        alpha_ptr = s->buf + 256 * 3;
836
        has_alpha = 0;
837
        for(i = 0; i < 256; i++) {
838
            v = palette[i];
839
            alpha = v >> 24;
840
            if (alpha != 0xff)
841
                has_alpha = 1;
842
            *alpha_ptr++ = alpha;
843
            ptr[0] = v >> 16;
844
            ptr[1] = v >> 8;
845
            ptr[2] = v;
846
            ptr += 3;
847
        }
848
        png_write_chunk(&s->bytestream, MKTAG('P', 'L', 'T', 'E'), s->buf, 256 * 3);
849
        if (has_alpha) {
850
            png_write_chunk(&s->bytestream, MKTAG('t', 'R', 'N', 'S'), s->buf + 256 * 3, 256);
851
        }
852
    }
853
854
    /* now put each row */
855
    s->zstream.avail_out = IOBUF_SIZE;
856
    s->zstream.next_out = s->buf;
857
    if (is_progressive) {
858
        uint8_t *ptr1;
859
        int pass;
860
861
        for(pass = 0; pass < NB_PASSES; pass++) {
862
            /* NOTE: a pass is completely omited if no pixels would be
863
               output */
864
            pass_row_size = png_pass_row_size(pass, bits_per_pixel, avctx->width);
865
            if (pass_row_size > 0) {
866
                for(y = 0; y < avctx->height; y++) {
867
                    if ((png_pass_ymask[pass] << (y & 7)) & 0x80) {
868
                        ptr = p->data[0] + y * p->linesize[0];
869
                        if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
870
                            convert_from_rgba32(tmp_buf, ptr, avctx->width);
871
                            ptr1 = tmp_buf;
872
                        } else {
873
                            ptr1 = ptr;
874
                        }
875 115329f1 Diego Biurrun
                        png_get_interlaced_row(crow_buf + 1, pass_row_size,
876
                                               bits_per_pixel, pass,
877 4eff7cf4 Michael Niedermayer
                                               ptr1, avctx->width);
878
                        crow_buf[0] = PNG_FILTER_VALUE_NONE;
879
                        png_write_row(s, crow_buf, pass_row_size + 1);
880
                    }
881
                }
882
            }
883
        }
884
    } else {
885
        for(y = 0; y < avctx->height; y++) {
886
            ptr = p->data[0] + y * p->linesize[0];
887
            if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
888
                convert_from_rgba32(crow_buf + 1, ptr, avctx->width);
889
            else
890
                memcpy(crow_buf + 1, ptr, row_size);
891
            crow_buf[0] = PNG_FILTER_VALUE_NONE;
892
            png_write_row(s, crow_buf, row_size + 1);
893
        }
894
    }
895
    /* compress last bytes */
896
    for(;;) {
897
        ret = deflate(&s->zstream, Z_FINISH);
898
        if (ret == Z_OK || ret == Z_STREAM_END) {
899
            len = IOBUF_SIZE - s->zstream.avail_out;
900 0ecca7a4 Michael Niedermayer
            if (len > 0 && s->bytestream_end - s->bytestream > len + 100) {
901 4eff7cf4 Michael Niedermayer
                png_write_chunk(&s->bytestream, MKTAG('I', 'D', 'A', 'T'), s->buf, len);
902
            }
903
            s->zstream.avail_out = IOBUF_SIZE;
904
            s->zstream.next_out = s->buf;
905
            if (ret == Z_STREAM_END)
906
                break;
907
        } else {
908
            goto fail;
909
        }
910
    }
911
    png_write_chunk(&s->bytestream, MKTAG('I', 'E', 'N', 'D'), NULL, 0);
912
913
    ret = s->bytestream - s->bytestream_start;
914
 the_end:
915
    av_free(crow_buf);
916
    av_free(tmp_buf);
917
    deflateEnd(&s->zstream);
918
    return ret;
919
 fail:
920
    ret = -1;
921
    goto the_end;
922
}
923
924
AVCodec png_decoder = {
925
    "png",
926
    CODEC_TYPE_VIDEO,
927
    CODEC_ID_PNG,
928
    sizeof(PNGContext),
929
    common_init,
930
    NULL,
931
    NULL, //decode_end,
932
    decode_frame,
933
    0 /*CODEC_CAP_DR1*/ /*| CODEC_CAP_DRAW_HORIZ_BAND*/,
934
    NULL
935
};
936
937 983ea0bc Roine Gustafsson
#ifdef CONFIG_PNG_ENCODER
938 4eff7cf4 Michael Niedermayer
AVCodec png_encoder = {
939
    "png",
940
    CODEC_TYPE_VIDEO,
941
    CODEC_ID_PNG,
942
    sizeof(PNGContext),
943
    common_init,
944
    encode_frame,
945
    NULL, //encode_end,
946
    .pix_fmts= (enum PixelFormat[]){PIX_FMT_RGB24, PIX_FMT_RGBA32, PIX_FMT_PAL8, PIX_FMT_GRAY8, PIX_FMT_MONOBLACK, -1},
947
};
948 983ea0bc Roine Gustafsson
#endif // CONFIG_PNG_ENCODER
949 4eff7cf4 Michael Niedermayer
#endif