Mercurial > libavcodec.hg
comparison pngenc.c @ 6396:2d7afa1bc573 libavcodec
png filters
author | lorenm |
---|---|
date | Mon, 25 Feb 2008 10:34:23 +0000 |
parents | af7e6436d8bd |
children | a0a1db4738dd |
comparison
equal
deleted
inserted
replaced
6395:9259af8a686b | 6396:2d7afa1bc573 |
---|---|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 */ | 20 */ |
21 #include "avcodec.h" | 21 #include "avcodec.h" |
22 #include "bytestream.h" | 22 #include "bytestream.h" |
23 #include "png.h" | 23 #include "png.h" |
24 #include <dsputil.h> | |
24 | 25 |
25 /* TODO: | 26 /* TODO: |
26 * - add 2, 4 and 16 bit depth support | 27 * - add 2, 4 and 16 bit depth support |
27 * - use filters when generating a png (better compression) | |
28 */ | 28 */ |
29 | 29 |
30 #include <zlib.h> | 30 #include <zlib.h> |
31 | 31 |
32 //#define DEBUG | 32 //#define DEBUG |
33 | 33 |
34 #define IOBUF_SIZE 4096 | 34 #define IOBUF_SIZE 4096 |
35 | 35 |
36 typedef struct PNGEncContext { | 36 typedef struct PNGEncContext { |
37 DSPContext dsp; | |
38 | |
37 uint8_t *bytestream; | 39 uint8_t *bytestream; |
38 uint8_t *bytestream_start; | 40 uint8_t *bytestream_start; |
39 uint8_t *bytestream_end; | 41 uint8_t *bytestream_end; |
40 AVFrame picture; | 42 AVFrame picture; |
43 | |
44 int filter_type; | |
41 | 45 |
42 z_stream zstream; | 46 z_stream zstream; |
43 uint8_t buf[IOBUF_SIZE]; | 47 uint8_t buf[IOBUF_SIZE]; |
44 } PNGEncContext; | 48 } PNGEncContext; |
45 | 49 |
79 } | 83 } |
80 break; | 84 break; |
81 } | 85 } |
82 } | 86 } |
83 | 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(0x80 - (buf1[i] ^ 0x80)); | |
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 | |
84 static void convert_from_rgb32(uint8_t *dst, const uint8_t *src, int width) | 175 static void convert_from_rgb32(uint8_t *dst, const uint8_t *src, int width) |
85 { | 176 { |
86 uint8_t *d; | 177 uint8_t *d; |
87 int j; | 178 int j; |
88 unsigned int v; | 179 unsigned int v; |
143 AVFrame *pict = data; | 234 AVFrame *pict = data; |
144 AVFrame * const p= (AVFrame*)&s->picture; | 235 AVFrame * const p= (AVFrame*)&s->picture; |
145 int bit_depth, color_type, y, len, row_size, ret, is_progressive; | 236 int bit_depth, color_type, y, len, row_size, ret, is_progressive; |
146 int bits_per_pixel, pass_row_size; | 237 int bits_per_pixel, pass_row_size; |
147 int compression_level; | 238 int compression_level; |
148 uint8_t *ptr; | 239 uint8_t *ptr, *top; |
149 uint8_t *crow_buf = NULL; | 240 uint8_t *crow_base = NULL, *crow_buf, *crow; |
150 uint8_t *tmp_buf = NULL; | 241 uint8_t *progressive_buf = NULL; |
242 uint8_t *rgba_buf = NULL; | |
243 uint8_t *top_buf = NULL; | |
151 | 244 |
152 *p = *pict; | 245 *p = *pict; |
153 p->pict_type= FF_I_TYPE; | 246 p->pict_type= FF_I_TYPE; |
154 p->key_frame= 1; | 247 p->key_frame= 1; |
155 | 248 |
193 av_clip(avctx->compression_level, 0, 9); | 286 av_clip(avctx->compression_level, 0, 9); |
194 ret = deflateInit2(&s->zstream, compression_level, | 287 ret = deflateInit2(&s->zstream, compression_level, |
195 Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY); | 288 Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY); |
196 if (ret != Z_OK) | 289 if (ret != Z_OK) |
197 return -1; | 290 return -1; |
198 crow_buf = av_malloc(row_size + 1); | 291 crow_base = av_malloc((row_size + 32) << (s->filter_type == PNG_FILTER_VALUE_MIXED)); |
199 if (!crow_buf) | 292 if (!crow_base) |
200 goto fail; | 293 goto fail; |
294 crow_buf = crow_base + 15; // pixel data should be aligned, but there's a control byte before it | |
201 if (is_progressive) { | 295 if (is_progressive) { |
202 tmp_buf = av_malloc(row_size + 1); | 296 progressive_buf = av_malloc(row_size + 1); |
203 if (!tmp_buf) | 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) | |
204 goto fail; | 308 goto fail; |
205 } | 309 } |
206 | 310 |
207 /* write png header */ | 311 /* write png header */ |
208 memcpy(s->bytestream, ff_pngsig, 8); | 312 memcpy(s->bytestream, ff_pngsig, 8); |
245 | 349 |
246 /* now put each row */ | 350 /* now put each row */ |
247 s->zstream.avail_out = IOBUF_SIZE; | 351 s->zstream.avail_out = IOBUF_SIZE; |
248 s->zstream.next_out = s->buf; | 352 s->zstream.next_out = s->buf; |
249 if (is_progressive) { | 353 if (is_progressive) { |
250 uint8_t *ptr1; | |
251 int pass; | 354 int pass; |
252 | 355 |
253 for(pass = 0; pass < NB_PASSES; pass++) { | 356 for(pass = 0; pass < NB_PASSES; pass++) { |
254 /* NOTE: a pass is completely omited if no pixels would be | 357 /* NOTE: a pass is completely omited if no pixels would be |
255 output */ | 358 output */ |
256 pass_row_size = ff_png_pass_row_size(pass, bits_per_pixel, avctx->width); | 359 pass_row_size = ff_png_pass_row_size(pass, bits_per_pixel, avctx->width); |
257 if (pass_row_size > 0) { | 360 if (pass_row_size > 0) { |
361 top = NULL; | |
258 for(y = 0; y < avctx->height; y++) { | 362 for(y = 0; y < avctx->height; y++) { |
259 if ((ff_png_pass_ymask[pass] << (y & 7)) & 0x80) { | 363 if ((ff_png_pass_ymask[pass] << (y & 7)) & 0x80) { |
260 ptr = p->data[0] + y * p->linesize[0]; | 364 ptr = p->data[0] + y * p->linesize[0]; |
365 FFSWAP(uint8_t*, progressive_buf, top_buf); | |
261 if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) { | 366 if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) { |
262 convert_from_rgb32(tmp_buf, ptr, avctx->width); | 367 convert_from_rgb32(rgba_buf, ptr, avctx->width); |
263 ptr1 = tmp_buf; | 368 ptr = rgba_buf; |
264 } else { | |
265 ptr1 = ptr; | |
266 } | 369 } |
267 png_get_interlaced_row(crow_buf + 1, pass_row_size, | 370 png_get_interlaced_row(progressive_buf, pass_row_size, |
268 bits_per_pixel, pass, | 371 bits_per_pixel, pass, |
269 ptr1, avctx->width); | 372 ptr, avctx->width); |
270 crow_buf[0] = PNG_FILTER_VALUE_NONE; | 373 crow = png_choose_filter(s, crow_buf, progressive_buf, top, pass_row_size, bits_per_pixel>>3); |
271 png_write_row(s, crow_buf, pass_row_size + 1); | 374 png_write_row(s, crow, pass_row_size + 1); |
375 top = progressive_buf; | |
272 } | 376 } |
273 } | 377 } |
274 } | 378 } |
275 } | 379 } |
276 } else { | 380 } else { |
381 top = NULL; | |
277 for(y = 0; y < avctx->height; y++) { | 382 for(y = 0; y < avctx->height; y++) { |
278 ptr = p->data[0] + y * p->linesize[0]; | 383 ptr = p->data[0] + y * p->linesize[0]; |
279 if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) | 384 if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) { |
280 convert_from_rgb32(crow_buf + 1, ptr, avctx->width); | 385 FFSWAP(uint8_t*, rgba_buf, top_buf); |
281 else | 386 convert_from_rgb32(rgba_buf, ptr, avctx->width); |
282 memcpy(crow_buf + 1, ptr, row_size); | 387 ptr = rgba_buf; |
283 crow_buf[0] = PNG_FILTER_VALUE_NONE; | 388 } |
284 png_write_row(s, crow_buf, row_size + 1); | 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; | |
285 } | 392 } |
286 } | 393 } |
287 /* compress last bytes */ | 394 /* compress last bytes */ |
288 for(;;) { | 395 for(;;) { |
289 ret = deflate(&s->zstream, Z_FINISH); | 396 ret = deflate(&s->zstream, Z_FINISH); |
302 } | 409 } |
303 png_write_chunk(&s->bytestream, MKTAG('I', 'E', 'N', 'D'), NULL, 0); | 410 png_write_chunk(&s->bytestream, MKTAG('I', 'E', 'N', 'D'), NULL, 0); |
304 | 411 |
305 ret = s->bytestream - s->bytestream_start; | 412 ret = s->bytestream - s->bytestream_start; |
306 the_end: | 413 the_end: |
307 av_free(crow_buf); | 414 av_free(crow_base); |
308 av_free(tmp_buf); | 415 av_free(progressive_buf); |
416 av_free(rgba_buf); | |
417 av_free(top_buf); | |
309 deflateEnd(&s->zstream); | 418 deflateEnd(&s->zstream); |
310 return ret; | 419 return ret; |
311 fail: | 420 fail: |
312 ret = -1; | 421 ret = -1; |
313 goto the_end; | 422 goto the_end; |
316 static int png_enc_init(AVCodecContext *avctx){ | 425 static int png_enc_init(AVCodecContext *avctx){ |
317 PNGEncContext *s = avctx->priv_data; | 426 PNGEncContext *s = avctx->priv_data; |
318 | 427 |
319 avcodec_get_frame_defaults((AVFrame*)&s->picture); | 428 avcodec_get_frame_defaults((AVFrame*)&s->picture); |
320 avctx->coded_frame= (AVFrame*)&s->picture; | 429 avctx->coded_frame= (AVFrame*)&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; | |
321 | 435 |
322 return 0; | 436 return 0; |
323 } | 437 } |
324 | 438 |
325 AVCodec png_encoder = { | 439 AVCodec png_encoder = { |