Mercurial > libavcodec.hg
view tiffenc.c @ 8204:507854688c43 libavcodec
Some BMP files have file size declared in the header equal to headers size
without image data, so try to correct that value before conducting checks on
declared file size.
author | kostya |
---|---|
date | Mon, 24 Nov 2008 11:24:02 +0000 |
parents | e943e1409077 |
children | 7a463923ecd1 |
line wrap: on
line source
/* * TIFF image encoder * Copyright (c) 2007 Bartlomiej Wolowiec * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * TIFF image encoder * @file tiffenc.c * @author Bartlomiej Wolowiec */ #include "avcodec.h" #ifdef CONFIG_ZLIB #include <zlib.h> #endif #include "bytestream.h" #include "tiff.h" #include "rle.h" #include "lzw.h" #define TIFF_MAX_ENTRY 32 /** sizes of various TIFF field types (string size = 1)*/ static const uint8_t type_sizes2[6] = { 0, 1, 1, 2, 4, 8 }; typedef struct TiffEncoderContext { AVCodecContext *avctx; AVFrame picture; int width; ///< picture width int height; ///< picture height unsigned int bpp; ///< bits per pixel int compr; ///< compression level int bpp_tab_size; ///< bpp_tab size int photometric_interpretation; ///< photometric interpretation int strips; ///< number of strips int rps; ///< row per strip uint8_t entries[TIFF_MAX_ENTRY*12]; ///< entires in header int num_entries; ///< number of entires uint8_t **buf; ///< actual position in buffer uint8_t *buf_start; ///< pointer to first byte in buffer int buf_size; ///< buffer size uint16_t subsampling[2]; ///< YUV subsampling factors struct LZWEncodeState *lzws; ///< LZW Encode state } TiffEncoderContext; /** * Check free space in buffer * @param s Tiff context * @param need Needed bytes * @return 0 - ok, 1 - no free space */ inline static int check_size(TiffEncoderContext * s, uint64_t need) { if (s->buf_size < *s->buf - s->buf_start + need) { *s->buf = s->buf_start + s->buf_size + 1; av_log(s->avctx, AV_LOG_ERROR, "Buffer is too small\n"); return 1; } return 0; } /** * Put n values to buffer * * @param p Pointer to pointer to output buffer * @param n Number of values * @param val Pointer to values * @param type Type of values * @param flip =0 - normal copy, >0 - flip */ static void tnput(uint8_t ** p, int n, const uint8_t * val, enum TiffTypes type, int flip) { int i; #ifdef WORDS_BIGENDIAN flip ^= ((int[]) {0, 0, 0, 1, 3, 3})[type]; #endif for (i = 0; i < n * type_sizes2[type]; i++) *(*p)++ = val[i ^ flip]; } /** * Add entry to directory in tiff header. * @param s Tiff context * @param tag Tag that identifies the entry * @param type Entry type * @param count The number of values * @param ptr_val Pointer to values */ static void add_entry(TiffEncoderContext * s, enum TiffTags tag, enum TiffTypes type, int count, const void *ptr_val) { uint8_t *entries_ptr = s->entries + 12 * s->num_entries; assert(s->num_entries < TIFF_MAX_ENTRY); bytestream_put_le16(&entries_ptr, tag); bytestream_put_le16(&entries_ptr, type); bytestream_put_le32(&entries_ptr, count); if (type_sizes[type] * count <= 4) { tnput(&entries_ptr, count, ptr_val, type, 0); } else { bytestream_put_le32(&entries_ptr, *s->buf - s->buf_start); check_size(s, count * type_sizes2[type]); tnput(s->buf, count, ptr_val, type, 0); } s->num_entries++; } static void add_entry1(TiffEncoderContext * s, enum TiffTags tag, enum TiffTypes type, int val){ uint16_t w = val; uint32_t dw= val; add_entry(s, tag, type, 1, type == TIFF_SHORT ? (void *)&w : (void *)&dw); } /** * Encode one strip in tiff file * * @param s Tiff context * @param src Input buffer * @param dst Output buffer * @param n Size of input buffer * @param compr Compression method * @return Number of output bytes. If an output error is encountered, -1 returned */ static int encode_strip(TiffEncoderContext * s, const int8_t * src, uint8_t * dst, int n, int compr) { switch (compr) { #ifdef CONFIG_ZLIB case TIFF_DEFLATE: case TIFF_ADOBE_DEFLATE: { unsigned long zlen = s->buf_size - (*s->buf - s->buf_start); if (compress(dst, &zlen, src, n) != Z_OK) { av_log(s->avctx, AV_LOG_ERROR, "Compressing failed\n"); return -1; } return zlen; } #endif case TIFF_RAW: if (check_size(s, n)) return -1; memcpy(dst, src, n); return n; case TIFF_PACKBITS: return ff_rle_encode(dst, s->buf_size - (*s->buf - s->buf_start), src, 1, n, 2, 0xff, -1, 0); case TIFF_LZW: return ff_lzw_encode(s->lzws, src, n); default: return -1; } } static void pack_yuv(TiffEncoderContext * s, uint8_t * dst, int lnum) { AVFrame *p = &s->picture; int i, j, k; int w = (s->width - 1) / s->subsampling[0] + 1; uint8_t *pu = &p->data[1][lnum / s->subsampling[1] * p->linesize[1]]; uint8_t *pv = &p->data[2][lnum / s->subsampling[1] * p->linesize[2]]; for (i = 0; i < w; i++){ for (j = 0; j < s->subsampling[1]; j++) for (k = 0; k < s->subsampling[0]; k++) *dst++ = p->data[0][(lnum + j) * p->linesize[0] + i * s->subsampling[0] + k]; *dst++ = *pu++; *dst++ = *pv++; } } static int encode_frame(AVCodecContext * avctx, unsigned char *buf, int buf_size, void *data) { TiffEncoderContext *s = avctx->priv_data; AVFrame *pict = data; AVFrame *const p = (AVFrame *) & s->picture; int i; int n; uint8_t *ptr = buf; uint8_t *offset; uint32_t strips; uint32_t *strip_sizes = NULL; uint32_t *strip_offsets = NULL; int bytes_per_row; uint32_t res[2] = { 72, 1 }; // image resolution (72/1) static const uint16_t bpp_tab[] = { 8, 8, 8, 8 }; int ret = -1; int is_yuv = 0; uint8_t *yuv_line = NULL; int shift_h, shift_v; s->buf_start = buf; s->buf = &ptr; s->buf_size = buf_size; *p = *pict; p->pict_type = FF_I_TYPE; p->key_frame = 1; avctx->coded_frame= &s->picture; s->compr = TIFF_PACKBITS; if (avctx->compression_level == 0) { s->compr = TIFF_RAW; } else if(avctx->compression_level == 2) { s->compr = TIFF_LZW; #ifdef CONFIG_ZLIB } else if ((avctx->compression_level >= 3)) { s->compr = TIFF_DEFLATE; #endif } s->width = avctx->width; s->height = avctx->height; s->subsampling[0] = 1; s->subsampling[1] = 1; switch (avctx->pix_fmt) { case PIX_FMT_RGB24: s->bpp = 24; s->photometric_interpretation = 2; break; case PIX_FMT_GRAY8: s->bpp = 8; s->photometric_interpretation = 1; break; case PIX_FMT_PAL8: s->bpp = 8; s->photometric_interpretation = 3; break; case PIX_FMT_MONOBLACK: s->bpp = 1; s->photometric_interpretation = 1; break; case PIX_FMT_MONOWHITE: s->bpp = 1; s->photometric_interpretation = 0; break; case PIX_FMT_YUV420P: case PIX_FMT_YUV422P: case PIX_FMT_YUV444P: case PIX_FMT_YUV410P: case PIX_FMT_YUV411P: s->photometric_interpretation = 6; avcodec_get_chroma_sub_sample(avctx->pix_fmt, &shift_h, &shift_v); s->bpp = 8 + (16 >> (shift_h + shift_v)); s->subsampling[0] = 1 << shift_h; s->subsampling[1] = 1 << shift_v; s->bpp_tab_size = 3; is_yuv = 1; break; default: av_log(s->avctx, AV_LOG_ERROR, "This colors format is not supported\n"); return -1; } if (!is_yuv) s->bpp_tab_size = (s->bpp >> 3); if (s->compr == TIFF_DEFLATE || s->compr == TIFF_ADOBE_DEFLATE || s->compr == TIFF_LZW) //best choose for DEFLATE s->rps = s->height; else s->rps = FFMAX(8192 / (((s->width * s->bpp) >> 3) + 1), 1); // suggest size of strip s->rps = ((s->rps - 1) / s->subsampling[1] + 1) * s->subsampling[1]; // round rps up strips = (s->height - 1) / s->rps + 1; if (check_size(s, 8)) goto fail; // write header bytestream_put_le16(&ptr, 0x4949); bytestream_put_le16(&ptr, 42); offset = ptr; bytestream_put_le32(&ptr, 0); strip_sizes = av_mallocz(sizeof(*strip_sizes) * strips); strip_offsets = av_mallocz(sizeof(*strip_offsets) * strips); bytes_per_row = (((s->width - 1)/s->subsampling[0] + 1) * s->bpp * s->subsampling[0] * s->subsampling[1] + 7) >> 3; if (is_yuv){ yuv_line = av_malloc(bytes_per_row); if (yuv_line == NULL){ av_log(s->avctx, AV_LOG_ERROR, "Not enough memory\n"); goto fail; } } #ifdef CONFIG_ZLIB if (s->compr == TIFF_DEFLATE || s->compr == TIFF_ADOBE_DEFLATE) { uint8_t *zbuf; int zlen, zn; int j; zlen = bytes_per_row * s->rps; zbuf = av_malloc(zlen); strip_offsets[0] = ptr - buf; zn = 0; for (j = 0; j < s->rps; j++) { if (is_yuv){ pack_yuv(s, yuv_line, j); memcpy(zbuf + zn, yuv_line, bytes_per_row); j += s->subsampling[1] - 1; } else memcpy(zbuf + j * bytes_per_row, p->data[0] + j * p->linesize[0], bytes_per_row); zn += bytes_per_row; } n = encode_strip(s, zbuf, ptr, zn, s->compr); av_free(zbuf); if (n<0) { av_log(s->avctx, AV_LOG_ERROR, "Encode strip failed\n"); goto fail; } ptr += n; strip_sizes[0] = ptr - buf - strip_offsets[0]; } else #endif { if(s->compr == TIFF_LZW) s->lzws = av_malloc(ff_lzw_encode_state_size); for (i = 0; i < s->height; i++) { if (strip_sizes[i / s->rps] == 0) { if(s->compr == TIFF_LZW){ ff_lzw_encode_init(s->lzws, ptr, s->buf_size - (*s->buf - s->buf_start), 12); } strip_offsets[i / s->rps] = ptr - buf; } if (is_yuv){ pack_yuv(s, yuv_line, i); n = encode_strip(s, yuv_line, ptr, bytes_per_row, s->compr); i += s->subsampling[1] - 1; } else n = encode_strip(s, p->data[0] + i * p->linesize[0], ptr, bytes_per_row, s->compr); if (n < 0) { av_log(s->avctx, AV_LOG_ERROR, "Encode strip failed\n"); goto fail; } strip_sizes[i / s->rps] += n; ptr += n; if(s->compr == TIFF_LZW && (i==s->height-1 || i%s->rps == s->rps-1)){ int ret; ret = ff_lzw_encode_flush(s->lzws); strip_sizes[(i / s->rps )] += ret ; ptr += ret; } } if(s->compr == TIFF_LZW) av_free(s->lzws); } s->num_entries = 0; add_entry1(s,TIFF_SUBFILE, TIFF_LONG, 0); add_entry1(s,TIFF_WIDTH, TIFF_LONG, s->width); add_entry1(s,TIFF_HEIGHT, TIFF_LONG, s->height); if (s->bpp_tab_size) add_entry(s, TIFF_BPP, TIFF_SHORT, s->bpp_tab_size, bpp_tab); add_entry1(s,TIFF_COMPR, TIFF_SHORT, s->compr); add_entry1(s,TIFF_INVERT, TIFF_SHORT, s->photometric_interpretation); add_entry(s, TIFF_STRIP_OFFS, TIFF_LONG, strips, strip_offsets); if (s->bpp_tab_size) add_entry1(s,TIFF_SAMPLES_PER_PIXEL, TIFF_SHORT, s->bpp_tab_size); add_entry1(s,TIFF_ROWSPERSTRIP, TIFF_LONG, s->rps); add_entry(s, TIFF_STRIP_SIZE, TIFF_LONG, strips, strip_sizes); add_entry(s, TIFF_XRES, TIFF_RATIONAL, 1, res); add_entry(s, TIFF_YRES, TIFF_RATIONAL, 1, res); add_entry1(s,TIFF_RES_UNIT, TIFF_SHORT, 2); if(!(avctx->flags & CODEC_FLAG_BITEXACT)) add_entry(s, TIFF_SOFTWARE_NAME, TIFF_STRING, strlen(LIBAVCODEC_IDENT) + 1, LIBAVCODEC_IDENT); if (avctx->pix_fmt == PIX_FMT_PAL8) { uint16_t pal[256 * 3]; for (i = 0; i < 256; i++) { uint32_t rgb = *(uint32_t *) (p->data[1] + i * 4); pal[i] = ((rgb >> 16) & 0xff) * 257; pal[i + 256] = ((rgb >> 8 ) & 0xff) * 257; pal[i + 512] = ( rgb & 0xff) * 257; } add_entry(s, TIFF_PAL, TIFF_SHORT, 256 * 3, pal); } if (is_yuv){ /** according to CCIR Recommendation 601.1 */ uint32_t refbw[12] = {15, 1, 235, 1, 128, 1, 240, 1, 128, 1, 240, 1}; add_entry(s, TIFF_YCBCR_SUBSAMPLING, TIFF_SHORT, 2, s->subsampling); add_entry(s, TIFF_REFERENCE_BW, TIFF_RATIONAL, 6, refbw); } bytestream_put_le32(&offset, ptr - buf); // write offset to dir if (check_size(s, 6 + s->num_entries * 12)) goto fail; bytestream_put_le16(&ptr, s->num_entries); // write tag count bytestream_put_buffer(&ptr, s->entries, s->num_entries * 12); bytestream_put_le32(&ptr, 0); ret = ptr - buf; fail: av_free(strip_sizes); av_free(strip_offsets); av_free(yuv_line); return ret; } AVCodec tiff_encoder = { "tiff", CODEC_TYPE_VIDEO, CODEC_ID_TIFF, sizeof(TiffEncoderContext), NULL, encode_frame, NULL, NULL, 0, NULL, .pix_fmts = (enum PixelFormat[]) {PIX_FMT_RGB24, PIX_FMT_PAL8, PIX_FMT_GRAY8, PIX_FMT_MONOBLACK, PIX_FMT_MONOWHITE, PIX_FMT_YUV420P, PIX_FMT_YUV422P, PIX_FMT_YUV444P, PIX_FMT_YUV410P, PIX_FMT_YUV411P, PIX_FMT_NONE}, .long_name = NULL_IF_CONFIG_SMALL("TIFF image"), };