Mercurial > libavcodec.hg
annotate pngenc.c @ 5542:b0a566346fb1 libavcodec
Add attribute that forces alignment of stack to functions that need it.
Necessary for systems that don't align by default to 16 bytes, required by some
SSE instructions.
Requires GCC >= 4.2.
Based on patch by Ga¸«³l Chardon.
author | ramiro |
---|---|
date | Mon, 13 Aug 2007 15:28:29 +0000 |
parents | a8c48a070cff |
children | af7e6436d8bd |
rev | line source |
---|---|
2342 | 1 /* |
2 * PNG image format | |
3 * Copyright (c) 2003 Fabrice Bellard. | |
4 * | |
3947
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3777
diff
changeset
|
5 * This file is part of FFmpeg. |
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3777
diff
changeset
|
6 * |
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3777
diff
changeset
|
7 * FFmpeg is free software; you can redistribute it and/or |
2342 | 8 * modify it under the terms of the GNU Lesser General Public |
9 * License as published by the Free Software Foundation; either | |
3947
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3777
diff
changeset
|
10 * version 2.1 of the License, or (at your option) any later version. |
2342 | 11 * |
3947
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3777
diff
changeset
|
12 * FFmpeg is distributed in the hope that it will be useful, |
2342 | 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 | |
3947
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3777
diff
changeset
|
18 * License along with FFmpeg; if not, write to the Free Software |
3036
0b546eab515d
Update licensing information: The FSF changed postal address.
diego
parents:
2967
diff
changeset
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
2342 | 20 */ |
21 #include "avcodec.h" | |
5067 | 22 #include "bytestream.h" |
5337 | 23 #include "png.h" |
2342 | 24 |
25 /* TODO: | |
26 * - add 2, 4 and 16 bit depth support | |
27 * - use filters when generating a png (better compression) | |
28 */ | |
29 | |
30 #include <zlib.h> | |
31 | |
32 //#define DEBUG | |
33 | |
5339 | 34 #define IOBUF_SIZE 4096 |
35 | |
36 typedef struct PNGEncContext { | |
37 uint8_t *bytestream; | |
38 uint8_t *bytestream_start; | |
39 uint8_t *bytestream_end; | |
40 AVFrame picture; | |
41 | |
42 z_stream zstream; | |
43 uint8_t buf[IOBUF_SIZE]; | |
44 } PNGEncContext; | |
45 | |
2967 | 46 static void png_get_interlaced_row(uint8_t *dst, int row_size, |
47 int bits_per_pixel, int pass, | |
2342 | 48 const uint8_t *src, int width) |
49 { | |
50 int x, mask, dst_x, j, b, bpp; | |
51 uint8_t *d; | |
52 const uint8_t *s; | |
53 | |
5337 | 54 mask = ff_png_pass_mask[pass]; |
2342 | 55 switch(bits_per_pixel) { |
56 case 1: | |
57 memset(dst, 0, row_size); | |
58 dst_x = 0; | |
59 for(x = 0; x < width; x++) { | |
60 j = (x & 7); | |
61 if ((mask << j) & 0x80) { | |
62 b = (src[x >> 3] >> (7 - j)) & 1; | |
63 dst[dst_x >> 3] |= b << (7 - (dst_x & 7)); | |
64 dst_x++; | |
65 } | |
66 } | |
67 break; | |
68 default: | |
69 bpp = bits_per_pixel >> 3; | |
70 d = dst; | |
71 s = src; | |
72 for(x = 0; x < width; x++) { | |
73 j = x & 7; | |
74 if ((mask << j) & 0x80) { | |
75 memcpy(d, s, bpp); | |
76 d += bpp; | |
77 } | |
78 s += bpp; | |
79 } | |
80 break; | |
81 } | |
82 } | |
83 | |
4515 | 84 static void convert_from_rgb32(uint8_t *dst, const uint8_t *src, int width) |
2342 | 85 { |
86 uint8_t *d; | |
87 int j; | |
88 unsigned int v; | |
2967 | 89 |
2342 | 90 d = dst; |
91 for(j = 0; j < width; j++) { | |
3347
82277c821113
Add const to (mostly) char* and make some functions static, which aren't used
diego
parents:
3177
diff
changeset
|
92 v = ((const uint32_t *)src)[j]; |
2342 | 93 d[0] = v >> 16; |
94 d[1] = v >> 8; | |
95 d[2] = v; | |
96 d[3] = v >> 24; | |
97 d += 4; | |
98 } | |
99 } | |
100 | |
101 static void png_write_chunk(uint8_t **f, uint32_t tag, | |
102 const uint8_t *buf, int length) | |
103 { | |
104 uint32_t crc; | |
105 uint8_t tagbuf[4]; | |
106 | |
5067 | 107 bytestream_put_be32(f, length); |
2342 | 108 crc = crc32(0, Z_NULL, 0); |
5089 | 109 AV_WL32(tagbuf, tag); |
2342 | 110 crc = crc32(crc, tagbuf, 4); |
5067 | 111 bytestream_put_be32(f, bswap_32(tag)); |
2342 | 112 if (length > 0) { |
113 crc = crc32(crc, buf, length); | |
114 memcpy(*f, buf, length); | |
115 *f += length; | |
116 } | |
5067 | 117 bytestream_put_be32(f, crc); |
2342 | 118 } |
119 | |
120 /* XXX: do filtering */ | |
5339 | 121 static int png_write_row(PNGEncContext *s, const uint8_t *data, int size) |
2342 | 122 { |
123 int ret; | |
124 | |
125 s->zstream.avail_in = size; | |
126 s->zstream.next_in = (uint8_t *)data; | |
127 while (s->zstream.avail_in > 0) { | |
128 ret = deflate(&s->zstream, Z_NO_FLUSH); | |
129 if (ret != Z_OK) | |
130 return -1; | |
131 if (s->zstream.avail_out == 0) { | |
2422 | 132 if(s->bytestream_end - s->bytestream > IOBUF_SIZE + 100) |
133 png_write_chunk(&s->bytestream, MKTAG('I', 'D', 'A', 'T'), s->buf, IOBUF_SIZE); | |
2342 | 134 s->zstream.avail_out = IOBUF_SIZE; |
135 s->zstream.next_out = s->buf; | |
136 } | |
137 } | |
138 return 0; | |
139 } | |
140 | |
141 static int encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data){ | |
5339 | 142 PNGEncContext *s = avctx->priv_data; |
2342 | 143 AVFrame *pict = data; |
144 AVFrame * const p= (AVFrame*)&s->picture; | |
145 int bit_depth, color_type, y, len, row_size, ret, is_progressive; | |
146 int bits_per_pixel, pass_row_size; | |
147 uint8_t *ptr; | |
148 uint8_t *crow_buf = NULL; | |
149 uint8_t *tmp_buf = NULL; | |
150 | |
151 *p = *pict; | |
152 p->pict_type= FF_I_TYPE; | |
153 p->key_frame= 1; | |
2967 | 154 |
2342 | 155 s->bytestream_start= |
156 s->bytestream= buf; | |
157 s->bytestream_end= buf+buf_size; | |
158 | |
159 is_progressive = !!(avctx->flags & CODEC_FLAG_INTERLACED_DCT); | |
160 switch(avctx->pix_fmt) { | |
4494
ce643a22f049
Replace deprecated PIX_FMT names by the newer variants.
diego
parents:
4379
diff
changeset
|
161 case PIX_FMT_RGB32: |
2342 | 162 bit_depth = 8; |
163 color_type = PNG_COLOR_TYPE_RGB_ALPHA; | |
164 break; | |
165 case PIX_FMT_RGB24: | |
166 bit_depth = 8; | |
167 color_type = PNG_COLOR_TYPE_RGB; | |
168 break; | |
169 case PIX_FMT_GRAY8: | |
170 bit_depth = 8; | |
171 color_type = PNG_COLOR_TYPE_GRAY; | |
172 break; | |
173 case PIX_FMT_MONOBLACK: | |
174 bit_depth = 1; | |
175 color_type = PNG_COLOR_TYPE_GRAY; | |
176 break; | |
177 case PIX_FMT_PAL8: | |
178 bit_depth = 8; | |
179 color_type = PNG_COLOR_TYPE_PALETTE; | |
180 break; | |
181 default: | |
182 return -1; | |
183 } | |
5337 | 184 bits_per_pixel = ff_png_get_nb_channels(color_type) * bit_depth; |
2342 | 185 row_size = (avctx->width * bits_per_pixel + 7) >> 3; |
186 | |
5337 | 187 s->zstream.zalloc = ff_png_zalloc; |
188 s->zstream.zfree = ff_png_zfree; | |
2342 | 189 s->zstream.opaque = NULL; |
190 ret = deflateInit2(&s->zstream, Z_DEFAULT_COMPRESSION, | |
191 Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY); | |
192 if (ret != Z_OK) | |
193 return -1; | |
194 crow_buf = av_malloc(row_size + 1); | |
195 if (!crow_buf) | |
196 goto fail; | |
197 if (is_progressive) { | |
198 tmp_buf = av_malloc(row_size + 1); | |
199 if (!tmp_buf) | |
200 goto fail; | |
201 } | |
202 | |
203 /* write png header */ | |
5337 | 204 memcpy(s->bytestream, ff_pngsig, 8); |
2342 | 205 s->bytestream += 8; |
2967 | 206 |
5067 | 207 AV_WB32(s->buf, avctx->width); |
208 AV_WB32(s->buf + 4, avctx->height); | |
2342 | 209 s->buf[8] = bit_depth; |
210 s->buf[9] = color_type; | |
211 s->buf[10] = 0; /* compression type */ | |
212 s->buf[11] = 0; /* filter type */ | |
213 s->buf[12] = is_progressive; /* interlace type */ | |
2967 | 214 |
2342 | 215 png_write_chunk(&s->bytestream, MKTAG('I', 'H', 'D', 'R'), s->buf, 13); |
216 | |
217 /* put the palette if needed */ | |
218 if (color_type == PNG_COLOR_TYPE_PALETTE) { | |
219 int has_alpha, alpha, i; | |
220 unsigned int v; | |
221 uint32_t *palette; | |
222 uint8_t *alpha_ptr; | |
2967 | 223 |
2342 | 224 palette = (uint32_t *)p->data[1]; |
225 ptr = s->buf; | |
226 alpha_ptr = s->buf + 256 * 3; | |
227 has_alpha = 0; | |
228 for(i = 0; i < 256; i++) { | |
229 v = palette[i]; | |
230 alpha = v >> 24; | |
4018 | 231 if (alpha && alpha != 0xff) |
2342 | 232 has_alpha = 1; |
233 *alpha_ptr++ = alpha; | |
5089 | 234 bytestream_put_be24(&ptr, v); |
2342 | 235 } |
236 png_write_chunk(&s->bytestream, MKTAG('P', 'L', 'T', 'E'), s->buf, 256 * 3); | |
237 if (has_alpha) { | |
238 png_write_chunk(&s->bytestream, MKTAG('t', 'R', 'N', 'S'), s->buf + 256 * 3, 256); | |
239 } | |
240 } | |
241 | |
242 /* now put each row */ | |
243 s->zstream.avail_out = IOBUF_SIZE; | |
244 s->zstream.next_out = s->buf; | |
245 if (is_progressive) { | |
246 uint8_t *ptr1; | |
247 int pass; | |
248 | |
249 for(pass = 0; pass < NB_PASSES; pass++) { | |
250 /* NOTE: a pass is completely omited if no pixels would be | |
251 output */ | |
5337 | 252 pass_row_size = ff_png_pass_row_size(pass, bits_per_pixel, avctx->width); |
2342 | 253 if (pass_row_size > 0) { |
254 for(y = 0; y < avctx->height; y++) { | |
5337 | 255 if ((ff_png_pass_ymask[pass] << (y & 7)) & 0x80) { |
2342 | 256 ptr = p->data[0] + y * p->linesize[0]; |
257 if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) { | |
4515 | 258 convert_from_rgb32(tmp_buf, ptr, avctx->width); |
2342 | 259 ptr1 = tmp_buf; |
260 } else { | |
261 ptr1 = ptr; | |
262 } | |
2967 | 263 png_get_interlaced_row(crow_buf + 1, pass_row_size, |
264 bits_per_pixel, pass, | |
2342 | 265 ptr1, avctx->width); |
266 crow_buf[0] = PNG_FILTER_VALUE_NONE; | |
267 png_write_row(s, crow_buf, pass_row_size + 1); | |
268 } | |
269 } | |
270 } | |
271 } | |
272 } else { | |
273 for(y = 0; y < avctx->height; y++) { | |
274 ptr = p->data[0] + y * p->linesize[0]; | |
275 if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) | |
4515 | 276 convert_from_rgb32(crow_buf + 1, ptr, avctx->width); |
2342 | 277 else |
278 memcpy(crow_buf + 1, ptr, row_size); | |
279 crow_buf[0] = PNG_FILTER_VALUE_NONE; | |
280 png_write_row(s, crow_buf, row_size + 1); | |
281 } | |
282 } | |
283 /* compress last bytes */ | |
284 for(;;) { | |
285 ret = deflate(&s->zstream, Z_FINISH); | |
286 if (ret == Z_OK || ret == Z_STREAM_END) { | |
287 len = IOBUF_SIZE - s->zstream.avail_out; | |
2422 | 288 if (len > 0 && s->bytestream_end - s->bytestream > len + 100) { |
2342 | 289 png_write_chunk(&s->bytestream, MKTAG('I', 'D', 'A', 'T'), s->buf, len); |
290 } | |
291 s->zstream.avail_out = IOBUF_SIZE; | |
292 s->zstream.next_out = s->buf; | |
293 if (ret == Z_STREAM_END) | |
294 break; | |
295 } else { | |
296 goto fail; | |
297 } | |
298 } | |
299 png_write_chunk(&s->bytestream, MKTAG('I', 'E', 'N', 'D'), NULL, 0); | |
300 | |
301 ret = s->bytestream - s->bytestream_start; | |
302 the_end: | |
303 av_free(crow_buf); | |
304 av_free(tmp_buf); | |
305 deflateEnd(&s->zstream); | |
306 return ret; | |
307 fail: | |
308 ret = -1; | |
309 goto the_end; | |
310 } | |
311 | |
5339 | 312 static int png_enc_init(AVCodecContext *avctx){ |
313 PNGEncContext *s = avctx->priv_data; | |
314 | |
315 avcodec_get_frame_defaults((AVFrame*)&s->picture); | |
316 avctx->coded_frame= (AVFrame*)&s->picture; | |
317 | |
318 return 0; | |
319 } | |
320 | |
2342 | 321 AVCodec png_encoder = { |
322 "png", | |
323 CODEC_TYPE_VIDEO, | |
324 CODEC_ID_PNG, | |
5339 | 325 sizeof(PNGEncContext), |
326 png_enc_init, | |
2342 | 327 encode_frame, |
328 NULL, //encode_end, | |
4494
ce643a22f049
Replace deprecated PIX_FMT names by the newer variants.
diego
parents:
4379
diff
changeset
|
329 .pix_fmts= (enum PixelFormat[]){PIX_FMT_RGB24, PIX_FMT_RGB32, PIX_FMT_PAL8, PIX_FMT_GRAY8, PIX_FMT_MONOBLACK, -1}, |
2342 | 330 }; |