Statistics
| Branch: | Revision:

ffmpeg / libavcodec / pngenc.c @ 72415b2a

History | View | Annotate | Download (13.2 KB)

1
/*
2
 * PNG image format
3
 * Copyright (c) 2003 Fabrice Bellard
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
#include "avcodec.h"
22
#include "bytestream.h"
23
#include "dsputil.h"
24
#include "png.h"
25

    
26
/* TODO:
27
 * - add 2, 4 and 16 bit depth support
28
 */
29

    
30
#include <zlib.h>
31

    
32
//#define DEBUG
33

    
34
#define IOBUF_SIZE 4096
35

    
36
typedef struct PNGEncContext {
37
    DSPContext dsp;
38

    
39
    uint8_t *bytestream;
40
    uint8_t *bytestream_start;
41
    uint8_t *bytestream_end;
42
    AVFrame picture;
43

    
44
    int filter_type;
45

    
46
    z_stream zstream;
47
    uint8_t buf[IOBUF_SIZE];
48
} PNGEncContext;
49

    
50
static void png_get_interlaced_row(uint8_t *dst, int row_size,
51
                                   int bits_per_pixel, int pass,
52
                                   const uint8_t *src, int width)
53
{
54
    int x, mask, dst_x, j, b, bpp;
55
    uint8_t *d;
56
    const uint8_t *s;
57

    
58
    mask = ff_png_pass_mask[pass];
59
    switch(bits_per_pixel) {
60
    case 1:
61
        memset(dst, 0, row_size);
62
        dst_x = 0;
63
        for(x = 0; x < width; x++) {
64
            j = (x & 7);
65
            if ((mask << j) & 0x80) {
66
                b = (src[x >> 3] >> (7 - j)) & 1;
67
                dst[dst_x >> 3] |= b << (7 - (dst_x & 7));
68
                dst_x++;
69
            }
70
        }
71
        break;
72
    default:
73
        bpp = bits_per_pixel >> 3;
74
        d = dst;
75
        s = src;
76
        for(x = 0; x < width; x++) {
77
            j = x & 7;
78
            if ((mask << j) & 0x80) {
79
                memcpy(d, s, bpp);
80
                d += bpp;
81
            }
82
            s += bpp;
83
        }
84
        break;
85
    }
86
}
87

    
88
static void sub_png_paeth_prediction(uint8_t *dst, uint8_t *src, uint8_t *top, int w, int bpp)
89
{
90
    int i;
91
    for(i = 0; i < w; i++) {
92
        int a, b, c, p, pa, pb, pc;
93

    
94
        a = src[i - bpp];
95
        b = top[i];
96
        c = top[i - bpp];
97

    
98
        p = b - c;
99
        pc = a - c;
100

    
101
        pa = abs(p);
102
        pb = abs(pc);
103
        pc = abs(p + pc);
104

    
105
        if (pa <= pb && pa <= pc)
106
            p = a;
107
        else if (pb <= pc)
108
            p = b;
109
        else
110
            p = c;
111
        dst[i] = src[i] - p;
112
    }
113
}
114

    
115
static void png_filter_row(DSPContext *dsp, uint8_t *dst, int filter_type,
116
                           uint8_t *src, uint8_t *top, int size, int bpp)
117
{
118
    int i;
119

    
120
    switch(filter_type) {
121
    case PNG_FILTER_VALUE_NONE:
122
        memcpy(dst, src, size);
123
        break;
124
    case PNG_FILTER_VALUE_SUB:
125
        dsp->diff_bytes(dst, src, src-bpp, size);
126
        memcpy(dst, src, bpp);
127
        break;
128
    case PNG_FILTER_VALUE_UP:
129
        dsp->diff_bytes(dst, src, top, size);
130
        break;
131
    case PNG_FILTER_VALUE_AVG:
132
        for(i = 0; i < bpp; i++)
133
            dst[i] = src[i] - (top[i] >> 1);
134
        for(; i < size; i++)
135
            dst[i] = src[i] - ((src[i-bpp] + top[i]) >> 1);
136
        break;
137
    case PNG_FILTER_VALUE_PAETH:
138
        for(i = 0; i < bpp; i++)
139
            dst[i] = src[i] - top[i];
140
        sub_png_paeth_prediction(dst+i, src+i, top+i, size-i, bpp);
141
        break;
142
    }
143
}
144

    
145
static uint8_t *png_choose_filter(PNGEncContext *s, uint8_t *dst,
146
                                  uint8_t *src, uint8_t *top, int size, int bpp)
147
{
148
    int pred = s->filter_type;
149
    assert(bpp || !pred);
150
    if(!top && pred)
151
        pred = PNG_FILTER_VALUE_SUB;
152
    if(pred == PNG_FILTER_VALUE_MIXED) {
153
        int i;
154
        int cost, bcost = INT_MAX;
155
        uint8_t *buf1 = dst, *buf2 = dst + size + 16;
156
        for(pred=0; pred<5; pred++) {
157
            png_filter_row(&s->dsp, buf1+1, pred, src, top, size, bpp);
158
            buf1[0] = pred;
159
            cost = 0;
160
            for(i=0; i<=size; i++)
161
                cost += abs((int8_t)buf1[i]);
162
            if(cost < bcost) {
163
                bcost = cost;
164
                FFSWAP(uint8_t*, buf1, buf2);
165
            }
166
        }
167
        return buf2;
168
    } else {
169
        png_filter_row(&s->dsp, dst+1, pred, src, top, size, bpp);
170
        dst[0] = pred;
171
        return dst;
172
    }
173
}
174

    
175
static void convert_from_rgb32(uint8_t *dst, const uint8_t *src, int width)
176
{
177
    uint8_t *d;
178
    int j;
179
    unsigned int v;
180

    
181
    d = dst;
182
    for(j = 0; j < width; j++) {
183
        v = ((const uint32_t *)src)[j];
184
        d[0] = v >> 16;
185
        d[1] = v >> 8;
186
        d[2] = v;
187
        d[3] = v >> 24;
188
        d += 4;
189
    }
190
}
191

    
192
static void png_write_chunk(uint8_t **f, uint32_t tag,
193
                            const uint8_t *buf, int length)
194
{
195
    uint32_t crc;
196
    uint8_t tagbuf[4];
197

    
198
    bytestream_put_be32(f, length);
199
    crc = crc32(0, Z_NULL, 0);
200
    AV_WL32(tagbuf, tag);
201
    crc = crc32(crc, tagbuf, 4);
202
    bytestream_put_be32(f, bswap_32(tag));
203
    if (length > 0) {
204
        crc = crc32(crc, buf, length);
205
        memcpy(*f, buf, length);
206
        *f += length;
207
    }
208
    bytestream_put_be32(f, crc);
209
}
210

    
211
/* XXX: do filtering */
212
static int png_write_row(PNGEncContext *s, const uint8_t *data, int size)
213
{
214
    int ret;
215

    
216
    s->zstream.avail_in = size;
217
    s->zstream.next_in = (uint8_t *)data;
218
    while (s->zstream.avail_in > 0) {
219
        ret = deflate(&s->zstream, Z_NO_FLUSH);
220
        if (ret != Z_OK)
221
            return -1;
222
        if (s->zstream.avail_out == 0) {
223
            if(s->bytestream_end - s->bytestream > IOBUF_SIZE + 100)
224
                png_write_chunk(&s->bytestream, MKTAG('I', 'D', 'A', 'T'), s->buf, IOBUF_SIZE);
225
            s->zstream.avail_out = IOBUF_SIZE;
226
            s->zstream.next_out = s->buf;
227
        }
228
    }
229
    return 0;
230
}
231

    
232
static int encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data){
233
    PNGEncContext *s = avctx->priv_data;
234
    AVFrame *pict = data;
235
    AVFrame * const p= &s->picture;
236
    int bit_depth, color_type, y, len, row_size, ret, is_progressive;
237
    int bits_per_pixel, pass_row_size;
238
    int compression_level;
239
    uint8_t *ptr, *top;
240
    uint8_t *crow_base = NULL, *crow_buf, *crow;
241
    uint8_t *progressive_buf = NULL;
242
    uint8_t *rgba_buf = NULL;
243
    uint8_t *top_buf = NULL;
244

    
245
    *p = *pict;
246
    p->pict_type= FF_I_TYPE;
247
    p->key_frame= 1;
248

    
249
    s->bytestream_start=
250
    s->bytestream= buf;
251
    s->bytestream_end= buf+buf_size;
252

    
253
    is_progressive = !!(avctx->flags & CODEC_FLAG_INTERLACED_DCT);
254
    switch(avctx->pix_fmt) {
255
    case PIX_FMT_RGB32:
256
        bit_depth = 8;
257
        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
258
        break;
259
    case PIX_FMT_RGB24:
260
        bit_depth = 8;
261
        color_type = PNG_COLOR_TYPE_RGB;
262
        break;
263
    case PIX_FMT_GRAY8:
264
        bit_depth = 8;
265
        color_type = PNG_COLOR_TYPE_GRAY;
266
        break;
267
    case PIX_FMT_MONOBLACK:
268
        bit_depth = 1;
269
        color_type = PNG_COLOR_TYPE_GRAY;
270
        break;
271
    case PIX_FMT_PAL8:
272
        bit_depth = 8;
273
        color_type = PNG_COLOR_TYPE_PALETTE;
274
        break;
275
    default:
276
        return -1;
277
    }
278
    bits_per_pixel = ff_png_get_nb_channels(color_type) * bit_depth;
279
    row_size = (avctx->width * bits_per_pixel + 7) >> 3;
280

    
281
    s->zstream.zalloc = ff_png_zalloc;
282
    s->zstream.zfree = ff_png_zfree;
283
    s->zstream.opaque = NULL;
284
    compression_level = avctx->compression_level == FF_COMPRESSION_DEFAULT ?
285
                            Z_DEFAULT_COMPRESSION :
286
                            av_clip(avctx->compression_level, 0, 9);
287
    ret = deflateInit2(&s->zstream, compression_level,
288
                       Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY);
289
    if (ret != Z_OK)
290
        return -1;
291
    crow_base = av_malloc((row_size + 32) << (s->filter_type == PNG_FILTER_VALUE_MIXED));
292
    if (!crow_base)
293
        goto fail;
294
    crow_buf = crow_base + 15; // pixel data should be aligned, but there's a control byte before it
295
    if (is_progressive) {
296
        progressive_buf = av_malloc(row_size + 1);
297
        if (!progressive_buf)
298
            goto fail;
299
    }
300
    if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
301
        rgba_buf = av_malloc(row_size + 1);
302
        if (!rgba_buf)
303
            goto fail;
304
    }
305
    if (is_progressive || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
306
        top_buf = av_malloc(row_size + 1);
307
        if (!top_buf)
308
            goto fail;
309
    }
310

    
311
    /* write png header */
312
    memcpy(s->bytestream, ff_pngsig, 8);
313
    s->bytestream += 8;
314

    
315
    AV_WB32(s->buf, avctx->width);
316
    AV_WB32(s->buf + 4, avctx->height);
317
    s->buf[8] = bit_depth;
318
    s->buf[9] = color_type;
319
    s->buf[10] = 0; /* compression type */
320
    s->buf[11] = 0; /* filter type */
321
    s->buf[12] = is_progressive; /* interlace type */
322

    
323
    png_write_chunk(&s->bytestream, MKTAG('I', 'H', 'D', 'R'), s->buf, 13);
324

    
325
    /* put the palette if needed */
326
    if (color_type == PNG_COLOR_TYPE_PALETTE) {
327
        int has_alpha, alpha, i;
328
        unsigned int v;
329
        uint32_t *palette;
330
        uint8_t *alpha_ptr;
331

    
332
        palette = (uint32_t *)p->data[1];
333
        ptr = s->buf;
334
        alpha_ptr = s->buf + 256 * 3;
335
        has_alpha = 0;
336
        for(i = 0; i < 256; i++) {
337
            v = palette[i];
338
            alpha = v >> 24;
339
            if (alpha && alpha != 0xff)
340
                has_alpha = 1;
341
            *alpha_ptr++ = alpha;
342
            bytestream_put_be24(&ptr, v);
343
        }
344
        png_write_chunk(&s->bytestream, MKTAG('P', 'L', 'T', 'E'), s->buf, 256 * 3);
345
        if (has_alpha) {
346
            png_write_chunk(&s->bytestream, MKTAG('t', 'R', 'N', 'S'), s->buf + 256 * 3, 256);
347
        }
348
    }
349

    
350
    /* now put each row */
351
    s->zstream.avail_out = IOBUF_SIZE;
352
    s->zstream.next_out = s->buf;
353
    if (is_progressive) {
354
        int pass;
355

    
356
        for(pass = 0; pass < NB_PASSES; pass++) {
357
            /* NOTE: a pass is completely omited if no pixels would be
358
               output */
359
            pass_row_size = ff_png_pass_row_size(pass, bits_per_pixel, avctx->width);
360
            if (pass_row_size > 0) {
361
                top = NULL;
362
                for(y = 0; y < avctx->height; y++) {
363
                    if ((ff_png_pass_ymask[pass] << (y & 7)) & 0x80) {
364
                        ptr = p->data[0] + y * p->linesize[0];
365
                        FFSWAP(uint8_t*, progressive_buf, top_buf);
366
                        if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
367
                            convert_from_rgb32(rgba_buf, ptr, avctx->width);
368
                            ptr = rgba_buf;
369
                        }
370
                        png_get_interlaced_row(progressive_buf, pass_row_size,
371
                                               bits_per_pixel, pass,
372
                                               ptr, avctx->width);
373
                        crow = png_choose_filter(s, crow_buf, progressive_buf, top, pass_row_size, bits_per_pixel>>3);
374
                        png_write_row(s, crow, pass_row_size + 1);
375
                        top = progressive_buf;
376
                    }
377
                }
378
            }
379
        }
380
    } else {
381
        top = NULL;
382
        for(y = 0; y < avctx->height; y++) {
383
            ptr = p->data[0] + y * p->linesize[0];
384
            if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
385
                FFSWAP(uint8_t*, rgba_buf, top_buf);
386
                convert_from_rgb32(rgba_buf, ptr, avctx->width);
387
                ptr = rgba_buf;
388
            }
389
            crow = png_choose_filter(s, crow_buf, ptr, top, row_size, bits_per_pixel>>3);
390
            png_write_row(s, crow, row_size + 1);
391
            top = ptr;
392
        }
393
    }
394
    /* compress last bytes */
395
    for(;;) {
396
        ret = deflate(&s->zstream, Z_FINISH);
397
        if (ret == Z_OK || ret == Z_STREAM_END) {
398
            len = IOBUF_SIZE - s->zstream.avail_out;
399
            if (len > 0 && s->bytestream_end - s->bytestream > len + 100) {
400
                png_write_chunk(&s->bytestream, MKTAG('I', 'D', 'A', 'T'), s->buf, len);
401
            }
402
            s->zstream.avail_out = IOBUF_SIZE;
403
            s->zstream.next_out = s->buf;
404
            if (ret == Z_STREAM_END)
405
                break;
406
        } else {
407
            goto fail;
408
        }
409
    }
410
    png_write_chunk(&s->bytestream, MKTAG('I', 'E', 'N', 'D'), NULL, 0);
411

    
412
    ret = s->bytestream - s->bytestream_start;
413
 the_end:
414
    av_free(crow_base);
415
    av_free(progressive_buf);
416
    av_free(rgba_buf);
417
    av_free(top_buf);
418
    deflateEnd(&s->zstream);
419
    return ret;
420
 fail:
421
    ret = -1;
422
    goto the_end;
423
}
424

    
425
static av_cold int png_enc_init(AVCodecContext *avctx){
426
    PNGEncContext *s = avctx->priv_data;
427

    
428
    avcodec_get_frame_defaults(&s->picture);
429
    avctx->coded_frame= &s->picture;
430
    dsputil_init(&s->dsp, avctx);
431

    
432
    s->filter_type = av_clip(avctx->prediction_method, PNG_FILTER_VALUE_NONE, PNG_FILTER_VALUE_MIXED);
433
    if(avctx->pix_fmt == PIX_FMT_MONOBLACK)
434
        s->filter_type = PNG_FILTER_VALUE_NONE;
435

    
436
    return 0;
437
}
438

    
439
AVCodec png_encoder = {
440
    "png",
441
    AVMEDIA_TYPE_VIDEO,
442
    CODEC_ID_PNG,
443
    sizeof(PNGEncContext),
444
    png_enc_init,
445
    encode_frame,
446
    NULL, //encode_end,
447
    .pix_fmts= (const enum PixelFormat[]){PIX_FMT_RGB24, PIX_FMT_RGB32, PIX_FMT_PAL8, PIX_FMT_GRAY8, PIX_FMT_MONOBLACK, PIX_FMT_NONE},
448
    .long_name= NULL_IF_CONFIG_SMALL("PNG image"),
449
};