[Ffmpeg-devel] [PATCH] TIFF encoder (Google SoC qualification task)

Kamil Nowosad k.nowosad
Thu Mar 29 11:55:30 CEST 2007


On Thu, Mar 29, 2007 at 01:39:17AM +0200, Michael Niedermayer wrote:
> Hi
> 
> are you going to send a second revission of your tiff encoder patch, which
> takes care of the review comments?
> or did you decide to work on some other task?

Hi,
Yes, I do. I have made some changes, according to the comments. However, I have planned to submit more extended version (this week I have two big tests on my studies, and it was impossible for me to do more, but I have planned to start work on Saturday (the whole time from Sat. until the deadline is free for me, so I can do my best :-) )) and then make the final fixes, after the review. I have forgotten what you have written in the "Patch review process" section in the documentation, sorry.

I have attached the patch. The next steps, according to the reviews,
are:

- YUV support
- support for horizontal predictor (and maybe LZW compression)
- support for JPEG compression
- optional choice of the best compression algorithm for every image
- maybe some others ;-)

You have written that new files (like tiff.h) have to be diffed against
the parent file. Do you mean doing svn copy tiff.c tiff.h insted of svn
add tiff.h ?

-- 
Pozdrawiam!
Kamil Nowosad
#gg: 1611683
-------------- next part --------------
Index: libavcodec/allcodecs.c
===================================================================
--- libavcodec/allcodecs.c	(wersja 8542)
+++ libavcodec/allcodecs.c	(kopia robocza)
@@ -131,7 +131,7 @@
     REGISTER_ENCDEC (TARGA, targa);
     REGISTER_DECODER(THEORA, theora);
     REGISTER_DECODER(TIERTEXSEQVIDEO, tiertexseqvideo);
-    REGISTER_DECODER(TIFF, tiff);
+    REGISTER_ENCDEC (TIFF, tiff);
     REGISTER_DECODER(TRUEMOTION1, truemotion1);
     REGISTER_DECODER(TRUEMOTION2, truemotion2);
     REGISTER_DECODER(TSCC, tscc);
Index: libavcodec/utils.c
===================================================================
--- libavcodec/utils.c	(wersja 8542)
+++ libavcodec/utils.c	(kopia robocza)
@@ -636,6 +636,9 @@
 {"coder", NULL, OFFSET(coder_type), FF_OPT_TYPE_INT, DEFAULT, INT_MIN, INT_MAX, V|E, "coder"},
 {"vlc", "variable length coder / huffman coder", 0, FF_OPT_TYPE_CONST, FF_CODER_TYPE_VLC, INT_MIN, INT_MAX, V|E, "coder"},
 {"ac", "arithmetic coder", 0, FF_OPT_TYPE_CONST, FF_CODER_TYPE_AC, INT_MIN, INT_MAX, V|E, "coder"},
+{"raw", "raw (no encoding)", 0, FF_OPT_TYPE_CONST, FF_CODER_TYPE_RAW, INT_MIN, INT_MAX, V|E, "coder"},
+{"rle", "run-lenghth coder", 0, FF_OPT_TYPE_CONST, FF_CODER_TYPE_RL, INT_MIN, INT_MAX, V|E, "coder"},
+{"def", "deflate", 0, FF_OPT_TYPE_CONST, FF_CODER_TYPE_DEFLATE, INT_MIN, INT_MAX, V|E, "coder"},
 {"context", "context model", OFFSET(context_model), FF_OPT_TYPE_INT, DEFAULT, INT_MIN, INT_MAX, V|E},
 {"slice_flags", NULL, OFFSET(slice_flags), FF_OPT_TYPE_INT, DEFAULT, INT_MIN, INT_MAX},
 {"xvmc_acceleration", NULL, OFFSET(xvmc_acceleration), FF_OPT_TYPE_INT, DEFAULT, INT_MIN, INT_MAX},
Index: libavcodec/Makefile
===================================================================
--- libavcodec/Makefile	(wersja 8542)
+++ libavcodec/Makefile	(kopia robocza)
@@ -144,6 +144,7 @@
 OBJS-$(CONFIG_THEORA_DECODER)          += vp3.o xiph.o
 OBJS-$(CONFIG_TIERTEXSEQVIDEO_DECODER) += tiertexseqv.o
 OBJS-$(CONFIG_TIFF_DECODER)            += tiff.o lzw.o
+OBJS-$(CONFIG_TIFF_ENCODER)            += tiffenc.o
 OBJS-$(CONFIG_TRUEMOTION1_DECODER)     += truemotion1.o
 OBJS-$(CONFIG_TRUEMOTION2_DECODER)     += truemotion2.o
 OBJS-$(CONFIG_TRUESPEECH_DECODER)      += truespeech.o
Index: libavcodec/avcodec.h
===================================================================
--- libavcodec/avcodec.h	(wersja 8542)
+++ libavcodec/avcodec.h	(kopia robocza)
@@ -1565,8 +1565,11 @@
      */
     int global_quality;

-#define FF_CODER_TYPE_VLC   0
-#define FF_CODER_TYPE_AC    1
+#define FF_CODER_TYPE_VLC       0
+#define FF_CODER_TYPE_AC        1
+#define FF_CODER_TYPE_RAW       2  // no coder
+#define FF_CODER_TYPE_RL        3  // run-length
+#define FF_CODER_TYPE_DEFLATE   4
     /**
      * coder type
      * - encoding: set by user.
@@ -2221,6 +2224,7 @@
 extern AVCodec sonic_ls_encoder;
 extern AVCodec svq1_encoder;
 extern AVCodec targa_encoder;
+extern AVCodec tiff_encoder;
 extern AVCodec vcr1_encoder;
 extern AVCodec vorbis_encoder;
 extern AVCodec wmav1_encoder;
Index: libavcodec/tiff.c
===================================================================
--- libavcodec/tiff.c	(wersja 8542)
+++ libavcodec/tiff.c	(kopia robocza)
@@ -20,54 +20,12 @@
  *
  */
 #include "avcodec.h"
+#include "tiff.h"
 #ifdef CONFIG_ZLIB
 #include <zlib.h>
 #endif
 #include "lzw.h"

-/* abridged list of TIFF tags */
-enum TiffTags{
-    TIFF_WIDTH = 0x100,
-    TIFF_HEIGHT,
-    TIFF_BPP,
-    TIFF_COMPR,
-    TIFF_INVERT = 0x106,
-    TIFF_STRIP_OFFS = 0x111,
-    TIFF_ROWSPERSTRIP = 0x116,
-    TIFF_STRIP_SIZE,
-    TIFF_PLANAR = 0x11C,
-    TIFF_XPOS = 0x11E,
-    TIFF_YPOS = 0x11F,
-    TIFF_PREDICTOR = 0x13D,
-    TIFF_PAL = 0x140
-};
-
-enum TiffCompr{
-    TIFF_RAW = 1,
-    TIFF_CCITT_RLE,
-    TIFF_G3,
-    TIFF_G4,
-    TIFF_LZW,
-    TIFF_JPEG,
-    TIFF_NEWJPEG,
-    TIFF_ADOBE_DEFLATE,
-    TIFF_PACKBITS = 0x8005,
-    TIFF_DEFLATE = 0x80B2
-};
-
-enum TiffTypes{
-    TIFF_BYTE = 1,
-    TIFF_STRING,
-    TIFF_SHORT,
-    TIFF_LONG,
-    TIFF_LONGLONG
-};
-
-/** sizes of various TIFF field types */
-static const int type_sizes[6] = {
-    0, 1, 100, 2, 4, 8
-};
-
 typedef struct TiffContext {
     AVCodecContext *avctx;
     AVFrame picture;
Index: libavcodec/tiff.h
===================================================================
--- libavcodec/tiff.h	(wersja 0)
+++ libavcodec/tiff.h	(wersja 0)
@@ -0,0 +1,74 @@
+/*
+ * TIFF image encoder/decoder
+ * Copyright (c) 2006 Konstantin Shishkov
+ * Copyright (c) 2007 Kamil Nowosad
+ *
+ * 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
+ *
+ */
+
+#ifndef TIFF_H
+#define TIFF_H
+
+/* abridged list of TIFF tags */
+enum TiffTags{
+    TIFF_WIDTH = 0x100,
+    TIFF_HEIGHT,
+    TIFF_BPP,
+    TIFF_COMPR,
+    TIFF_INVERT = 0x106,
+    TIFF_STRIP_OFFS = 0x111,
+    TIFF_SAMPLESPERPIX = 0x115,
+    TIFF_ROWSPERSTRIP,
+    TIFF_STRIP_SIZE,
+    TIFF_PLANAR = 0x11C,
+    TIFF_XRES = 0x11A,
+    TIFF_YRES = 0x11B,
+    TIFF_XPOS = 0x11E,
+    TIFF_YPOS = 0x11F,
+    TIFF_RES_UNIT = 0x128,
+    TIFF_PREDICTOR = 0x13D,
+    TIFF_PAL = 0x140
+};
+
+/* list of TIFF compression types */
+enum TiffCompr{
+    TIFF_RAW = 1,
+    TIFF_CCITT_RLE,
+    TIFF_G3,
+    TIFF_G4,
+    TIFF_LZW,
+    TIFF_JPEG,
+    TIFF_NEWJPEG,
+    TIFF_ADOBE_DEFLATE,
+    TIFF_PACKBITS = 0x8005,
+    TIFF_DEFLATE = 0x80B2
+};
+
+enum TiffTypes{
+    TIFF_BYTE = 1,
+    TIFF_STRING,
+    TIFF_SHORT,
+    TIFF_LONG,
+    TIFF_LONGLONG
+};
+
+/** sizes of various TIFF field types */
+static const int type_sizes[6] = {
+    0, 1, 100, 2, 4, 8
+};
+#endif // TIFF_H
Index: libavcodec/tiffenc.c
===================================================================
--- libavcodec/tiffenc.c	(wersja 0)
+++ libavcodec/tiffenc.c	(wersja 0)
@@ -0,0 +1,617 @@
+/*
+ * TIFF image encoder
+ * Copyright (c) 2007 Kamil Nowosad
+ *
+ * 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 format encoder
+ * @file tiffenc.c
+ * @author Kamil Nowosad
+ */
+#include "avcodec.h"
+#include "bytestream.h"
+#include "tiff.h"
+#ifdef CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
+#define CURR_OFFSET(s) (s->buf - s->buf_start + s->offset)
+
+typedef struct TiffIfdEntry {
+    uint16_t tag;
+    uint16_t type;
+    uint32_t count;
+    uint32_t value;             /// value or offset
+} TiffIfdEntry;
+
+typedef struct TiffEncoderContext {
+    enum TiffCompr compression;
+    uint8_t *buf, *buf_start, *buf_end, *entry_point;
+
+    /// the offset in stream at the beginning
+    int offset;
+
+    int strip,
+        num_of_strips,
+        lines_per_strip,
+        *strip_offset,
+        *strip_size;
+
+    int has_color_map;
+    uint16_t *color_map;
+
+    AVFrame *p;
+
+    TiffIfdEntry *ifd_entries;
+    /// size of ifd_entries buffer
+    int num_ifd_entries, max_ifd_entries;
+
+    int bpp,
+        bytes_per_line,
+        /// the number of samples
+        nsamples;
+
+    uint16_t bpp_info[4];
+
+    int photometric_interpretation;
+
+    int write_trailer;
+} TiffEncoderContext;
+
+
+#ifdef CONFIG_ZLIB
+const enum TiffCompr tiff_default_compression = TIFF_DEFLATE;
+#else
+const enum TiffCompr tiff_default_compression = TIFF_PACKBITS;
+#endif
+
+static int value_size(uint16_t type, uint32_t count, uint8_t * value)
+{
+    switch (type) {
+    case TIFF_BYTE:
+        return count;
+    case TIFF_SHORT:
+        return 2 * count;
+    case TIFF_LONG:
+        return 4 * count;
+    case TIFF_LONGLONG:
+        return 8 * count;
+    case TIFF_STRING:
+        return strlen(value) + 1;
+    }
+    return 0;                   // never reached
+}
+
+/**
+ * adds an entry to the IFD (referenced by a pointer)
+ */
+static int tiff_add_ifd_entryp(AVCodecContext * avctx, uint16_t tag,
+                                uint16_t type, uint32_t count,
+                                uint8_t * value)
+{
+    TiffEncoderContext *s = avctx->priv_data;
+    int size = value_size(type, count, value);
+    TiffIfdEntry *entr;
+
+    if (s->num_ifd_entries == s->max_ifd_entries) {     // time to enlarge the buffer
+        s->max_ifd_entries *= 2;
+        s->ifd_entries =
+            av_realloc(s->ifd_entries,
+                       s->max_ifd_entries * sizeof(TiffIfdEntry));
+        if (s->ifd_entries == NULL) {
+            av_log(avctx, AV_LOG_ERROR, "not enough memory\n");
+            return -1;
+        }
+    }
+
+    entr = s->ifd_entries + s->num_ifd_entries;
+    entr->tag = tag;
+    entr->type = type;
+    entr->count = count;
+    if (size > 4) {
+        if (s->buf + size >= s->buf_end){
+            av_log(avctx, AV_LOG_ERROR, "the buffer given is too short\n");
+            return -1;
+        }
+        entr->value = CURR_OFFSET(s);
+        memcpy(s->buf, value, size);
+        s->buf += size;
+    } else {
+        entr->value = 0;
+        memcpy((uint8_t *) & entr->value, value, size);
+    }
+    s->num_ifd_entries++;
+    return 0;
+}
+/**
+ * adds an entry to the IFD (referenced by a value)
+ * [it is helpful when passing constants]
+ */
+static int tiff_add_ifd_entryv(AVCodecContext * avctx, uint16_t tag,
+                                uint16_t type, uint32_t count,
+                                uint32_t value)
+{
+    if (type == TIFF_BYTE) {
+        uint8_t x = value;
+        return tiff_add_ifd_entryp(avctx, tag, type, count, &x);
+    } else if (type == TIFF_SHORT) {
+        uint16_t x = value;
+        return tiff_add_ifd_entryp(avctx, tag, type, count, &x);
+    } else if (type == TIFF_LONG) {
+        uint32_t x = value;
+        return tiff_add_ifd_entryp(avctx, tag, type, count, &x);
+    }
+    return -1;
+}
+/**
+ * writes the whole ifd to stream
+ */
+static int tiff_write_ifd(AVCodecContext * avctx)
+{
+    TiffEncoderContext *s = avctx->priv_data;
+    TiffIfdEntry *entr = s->ifd_entries;
+    int entry;
+
+    /* image file directory starts here:  */
+    bytestream_put_le32(&s->entry_point, CURR_OFFSET(s));
+
+    if (s->buf + s->num_ifd_entries * 12 + 2 + 4 >= s->buf_end){
+        av_log(avctx, AV_LOG_ERROR, "the buffer given is too short\n");
+        return -1;
+    }
+
+    /* the number of IFD entries */
+    bytestream_put_le16(&s->buf, s->num_ifd_entries);
+    for (entry = 0; entry < s->num_ifd_entries; entry++, entr++) {
+        bytestream_put_le16(&s->buf, entr->tag);
+        bytestream_put_le16(&s->buf, entr->type);
+        bytestream_put_le32(&s->buf, entr->count);
+        bytestream_put_le32(&s->buf, entr->value);
+    }
+    return 0;
+}
+
+/**
+ * packbits compression implementation
+ */
+static int tiff_pack_bits(uint8_t * dst, int *dst_len, uint8_t * src,
+                          int src_len)
+{
+    uint8_t *dst_begin = dst, *last_literal, special_case_ch;
+    enum { START, RUN, LITERAL, SPECIAL } last;
+    last = START;
+    while (src_len > 0) {
+        uint8_t *sp;
+        int num = 1;
+
+        for (sp = src + 1, src_len--; (*sp == *src) && (src_len > 0);
+             src++, src_len--)
+            num++;
+
+        if (dst - dst_begin + (num + 127) / 128 + 2 >= *dst_len)        // check if has enough space
+            return -1;
+
+
+#define CHECK_AND_ADD(x, expr){ \
+            if ((expr) || *last_literal == 127){\
+                *dst = -1;\
+                last_literal = dst++;\
+            }\
+            (*last_literal)++;\
+            *dst++ = x;\
+        }
+        if (num == 1) {
+            if (last == SPECIAL) {
+                CHECK_AND_ADD(special_case_ch, 0);
+                CHECK_AND_ADD(special_case_ch, 0);
+                CHECK_AND_ADD(*sp, 0);
+            } else
+                CHECK_AND_ADD(*sp, last != LITERAL);
+            last = LITERAL;
+        } else if (num == 2) {
+            switch (last) {
+            case LITERAL:
+                special_case_ch = *sp;
+                last = SPECIAL;
+                break;
+            case SPECIAL:
+                CHECK_AND_ADD(special_case_ch, 0);
+                CHECK_AND_ADD(special_case_ch, 0);
+                special_case_ch = *sp;
+            default:
+                *dst++ = -1;
+                *dst++ = *sp;
+                last = RUN;
+            }
+        } else {
+            if (last == SPECIAL) {
+                *dst++ = -1;
+                *dst++ = special_case_ch;
+            }
+            while (num > 0) {
+                *dst++ = (uint8_t) (-FFMIN(num, 128) + 1);
+                *dst++ = *sp;
+                num -= 128;
+            }
+            last = RUN;
+        }
+    }
+    if (last == SPECIAL) {
+        if (*last_literal < 126) {
+            *last_literal += 2;
+            *dst++ = special_case_ch;
+            *dst++ = special_case_ch;
+        } else {
+            *dst++ = -1;
+            *dst++ = special_case_ch;
+        }
+    }
+#undef CHECK_AND_ADD
+    *dst_len = dst - dst_begin;
+    return 0;
+}
+
+static int tiff_prepare_color_map(AVCodecContext * avctx)
+{
+    TiffEncoderContext *s = avctx->priv_data;
+    AVFrame *p = s->p;
+    int i;
+    uint32_t *src = p->data[1];
+
+    s->color_map = av_malloc((1 << s->bpp) * 3 * sizeof(uint16_t));
+    if (s->color_map == NULL) {
+        av_log(avctx, AV_LOG_ERROR, "not enough memory\n");
+        return -1;
+    }
+
+    for (i = 0; i < (1 << s->bpp); i++) {
+        uint32_t sample = *src;
+        s->color_map[2 * (1 << s->bpp) + i] = (sample & 255) << 8;      // B
+        sample = sample >> 8;
+        s->color_map[(1 << s->bpp) + i] = (sample & 255) << 8;  // G
+        sample = sample >> 8;
+        s->color_map[i] = (sample & 255) << 8;  // R
+        src++;
+    }
+    return 0;
+}
+
+static int tiff_write_image_data(AVCodecContext * avctx)
+{
+    TiffEncoderContext *s = avctx->priv_data;
+    AVFrame *p = s->p;
+    int i, y, len;
+#ifdef CONFIG_ZLIB
+    uint8_t *zbuf_start, *zbuf;
+    uLongf zlen;
+#endif
+
+    /* set the compression */
+    switch (avctx->coder_type) {
+    case 0:                    // XXX: when a user chose vlc it also assigns the default
+        s->compression = tiff_default_compression;
+        break;
+    case FF_CODER_TYPE_RAW:
+        s->compression = TIFF_RAW;
+        break;
+    case FF_CODER_TYPE_RL:
+        s->compression = TIFF_PACKBITS;
+        break;
+    case FF_CODER_TYPE_DEFLATE:
+#ifdef CONFIG_ZLIB
+        s->compression = TIFF_DEFLATE;
+        break;
+#else
+        av_log(avctx, AV_LOG_ERROR,
+               "Couldn't use deflate - ZLib not compiled in\n");
+        return -1;
+#endif
+    default:
+        av_log(avctx, AV_LOG_ERROR,
+               "Chosen compression type not supported\n");
+        return -1;
+    }
+
+    /* compute the number of strips */
+    if (avctx->width == 0 || avctx->height == 0) {
+        av_log(avctx, AV_LOG_ERROR, "invalid image size\n");
+        return -1;
+    } else if (s->compression == TIFF_DEFLATE) {
+        s->lines_per_strip = avctx->height;
+        s->num_of_strips = 1;
+    } else {
+        s->lines_per_strip = (8192 + s->bytes_per_line - 1) / (s->bytes_per_line);      // =ceil(height/num_of_strips)
+        s->num_of_strips =
+            (avctx->height + s->lines_per_strip - 1) / s->lines_per_strip;
+    }
+
+    /* write the image data */
+    s->strip_offset = av_malloc((s->num_of_strips + 1) * sizeof(int));
+    s->strip_size = av_malloc((s->num_of_strips + 1) * sizeof(int));
+    if (s->strip_offset == NULL || s->strip_size == NULL) {
+        av_log(avctx, AV_LOG_ERROR, "not enough memory\n");
+        return -1;
+    }
+    s->strip = 0;
+    switch (s->compression) {
+    case TIFF_RAW:
+        if (s->buf + avctx->height * s->bytes_per_line >= s->buf_end){
+            av_log(avctx, AV_LOG_ERROR, "the buffer given is too short\n");
+            return -1;
+        }
+        for (y = 0; y < avctx->height; y += s->lines_per_strip) {
+            uint8_t *src;
+            int i;
+
+            s->strip_offset[s->strip++] = CURR_OFFSET(s);
+            for (i = 0;
+                 (i < s->lines_per_strip) && (y + i < avctx->height);
+                 i++) {
+                src = p->data[0] + (y + i) * p->linesize[0];
+                memcpy(s->buf, src, s->bytes_per_line);
+                s->buf += s->bytes_per_line;
+            }
+        }
+        break;
+    case TIFF_PACKBITS:
+        for (y = 0; y < avctx->height; y += s->lines_per_strip) {
+            uint8_t *src;
+            int i;
+
+            s->strip_offset[s->strip++] = CURR_OFFSET(s);
+            for (i = 0;
+                 (i < s->lines_per_strip) && (y + i < avctx->height);
+                 i++) {
+                int plen = s->buf_end - s->buf;
+                src = p->data[0] + (y + i) * p->linesize[0];
+                if (tiff_pack_bits(s->buf, &plen, src, s->bytes_per_line)
+                    == -1) {
+                    av_log(avctx, AV_LOG_ERROR,
+                           "packbits compression error\n");
+                    return -1;
+                }
+                s->buf += plen;
+            }
+        }
+        s->strip_offset[s->strip] = CURR_OFFSET(s);
+        break;
+#ifdef CONFIG_ZLIB
+    case TIFF_DEFLATE:
+        zbuf_start = zbuf =
+            av_malloc(s->bytes_per_line * s->lines_per_strip);
+        if (zbuf == NULL) {
+            av_log(avctx, AV_LOG_ERROR, "not enough memory\n");
+            return -1;
+        }
+        for (y = 0; y < avctx->height; y++) {
+            uint8_t *src;
+            src = p->data[0] + y * p->linesize[0];
+            memcpy(zbuf, src, s->bytes_per_line);
+            zbuf += s->bytes_per_line;
+        }
+        s->strip_offset[0] = CURR_OFFSET(s);
+        zlen = s->buf_end - s->buf;
+        if (compress(s->buf, &zlen, zbuf_start, zbuf - zbuf_start) != Z_OK) {
+            av_log(avctx, AV_LOG_ERROR, "ZLib compression error\n");
+            av_free(zbuf_start);
+            return -1;
+        }
+        s->buf += zlen;
+        s->strip = 1; // just one strip for better compression
+        av_free(zbuf_start);
+        break;
+#endif
+    }
+    s->strip_offset[s->strip] = CURR_OFFSET(s);
+
+    /* compute strip sizes */
+    for (i = 0; i < s->strip; i++)
+        s->strip_size[i] = s->strip_offset[i + 1] - s->strip_offset[i];
+
+    return 0;
+}
+
+/**
+ * encoding scheme:
+ * @li [if the client has not set it] TIFF signature
+ * @li offset of the IFD
+ * @li image data [divided into strips]
+ * @li IFD data which doesn't fit inside the IFD
+ * @li IFD, sorted by tag
+ * @li [if the client will not set it] 32 zero bits
+ */
+static int tiff_encode_frame(AVCodecContext * avctx, unsigned char *buf,
+                             int buf_size, void *data)
+{
+    TiffEncoderContext *s = avctx->priv_data;
+    const uint8_t header[4] = {0x49, 0x49, 42, 0},
+                  trailer[4] = {0, 0, 0, 0};
+    uint32_t xres[2] = {72, 1},
+             yres[2] = {72, 1};
+    int ret;
+
+    s->buf_start = s->buf = buf;
+    s->buf_end = s->buf + buf_size;
+    s->p = data;
+
+    if (avctx->frame_number == 0) {
+        /* do we have to write the header? (the TIFF muxer does it
+         * for us - it is useful when creating TIFFs with multiple images in one file) */
+        if (!avctx->extradata_size) { // i'm sure that there exists a nicer solution - maybe adding flags?
+            memcpy(s->buf, header, 4);
+            s->buf += 4;
+            s->offset = 0;
+            s->write_trailer = 1;
+        } else
+            s->offset = *(int *) avctx->extradata;
+    }
+
+    switch (avctx->pix_fmt) {
+    case PIX_FMT_PAL8:
+        s->bpp_info[0] = 8;
+        s->nsamples = 1;
+        s->bpp = 8;
+        s->photometric_interpretation = 3;
+        s->has_color_map = 1;
+        break;
+    case PIX_FMT_RGB24:
+        s->bpp_info[0] = 8;
+        s->bpp_info[1] = 8;
+        s->bpp_info[2] = 8;
+        s->nsamples = 3;
+        s->bpp = 24;
+        s->photometric_interpretation = 2;
+        break;
+    case PIX_FMT_GRAY8:
+        s->bpp_info[0] = 8;
+        s->nsamples = 1;
+        s->bpp = 8;
+        s->photometric_interpretation = 1;
+        break;
+    case PIX_FMT_MONOBLACK:
+        s->bpp_info[0] = 1;
+        s->nsamples = 1;
+        s->bpp = 1;
+        s->photometric_interpretation = 1;
+        break;
+    case PIX_FMT_MONOWHITE:
+        s->bpp_info[0] = 1;
+        s->nsamples = 1;
+        s->bpp = 1;
+        s->photometric_interpretation = 0;
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "this pix_fmt (%d) is not supported\n",
+               avctx->pix_fmt);
+        return -1;
+    }
+
+    s->bytes_per_line = (avctx->width * s->bpp + 7) / 8;
+
+    s->entry_point = s->buf;
+    s->buf += 4;                // a place for next IFD offset [later updated]
+
+    if (s->has_color_map)
+        if (tiff_prepare_color_map(avctx) < 0) {
+            return -1;
+        }
+
+    /* write the image data */
+    if (tiff_write_image_data(avctx)) {
+        if (s->strip_offset)
+            av_free(s->strip_offset);
+        if (s->strip_size)
+            av_free(s->strip_size);
+        if (s->has_color_map)
+            av_free(s->color_map);
+        return -1;
+    }
+
+    /* IFD entries */
+    s->num_ifd_entries = 0;
+
+    if(
+    tiff_add_ifd_entryv(avctx, TIFF_WIDTH,          TIFF_LONG,      1,          avctx->width) ||
+    tiff_add_ifd_entryv(avctx, TIFF_HEIGHT,         TIFF_LONG,      1,          avctx->height) ||
+    tiff_add_ifd_entryp(avctx, TIFF_BPP,            TIFF_SHORT,     s->nsamples,s->bpp_info) ||
+    tiff_add_ifd_entryv(avctx, TIFF_COMPR,          TIFF_SHORT,     1,          s->compression) ||
+    tiff_add_ifd_entryv(avctx, TIFF_INVERT,         TIFF_SHORT,     1,          s->photometric_interpretation) ||
+    tiff_add_ifd_entryp(avctx, TIFF_STRIP_OFFS,     TIFF_LONG,      s->strip,   s->strip_offset) ||
+    tiff_add_ifd_entryv(avctx, TIFF_SAMPLESPERPIX,  TIFF_SHORT,     1,          s->nsamples) ||
+    tiff_add_ifd_entryv(avctx, TIFF_ROWSPERSTRIP,   TIFF_LONG,      1,          s->lines_per_strip) ||
+    tiff_add_ifd_entryp(avctx, TIFF_STRIP_SIZE,     TIFF_LONG,      s->strip,   s->strip_size) ||
+    tiff_add_ifd_entryp(avctx, TIFF_XRES,           TIFF_LONGLONG,  1,          xres) ||
+    tiff_add_ifd_entryp(avctx, TIFF_YRES,           TIFF_LONGLONG,  1,          yres) ||
+    tiff_add_ifd_entryv(avctx, TIFF_RES_UNIT,       TIFF_SHORT,     1,          2) ||
+((s->has_color_map) &&
+    tiff_add_ifd_entryp(avctx, TIFF_PAL,            TIFF_SHORT, 3*(1<<s->bpp), s->color_map))
+    ){
+        ret = -1;
+        goto cleanup;
+    }
+
+
+    if (tiff_write_ifd(avctx)){
+        ret = -1;
+        goto cleanup;
+    }
+
+    /* do we have to write the trailer? */
+    if (s->write_trailer) {
+        memcpy(s->buf, trailer, 4);
+        s->buf += 4;
+    }
+
+    /* end writing */
+    s->offset = CURR_OFFSET(s);
+    ret = s->buf - s->buf_start;
+cleanup:
+    av_free(s->strip_offset);
+    av_free(s->strip_size);
+    if (s->has_color_map)
+        av_free(s->color_map);
+
+    return ret;
+}
+
+static int tiff_encoder_init(AVCodecContext * avctx)
+{
+    TiffEncoderContext *s = avctx->priv_data;
+
+    s->max_ifd_entries = 16;
+    s->ifd_entries = av_malloc(s->max_ifd_entries * sizeof(TiffIfdEntry));
+    if (s->ifd_entries == NULL) {
+        av_log(avctx, AV_LOG_ERROR, "not enough memory\n");
+        return -1;
+    }
+
+    s->offset = 4;
+
+    return 0;
+}
+
+static int tiff_encoder_end(AVCodecContext * avctx)
+{
+    TiffEncoderContext *s = avctx->priv_data;
+
+    if (s->ifd_entries != NULL)
+        av_free(s->ifd_entries);
+    return 0;
+}
+
+AVCodec tiff_encoder = {
+    "tiff",
+    CODEC_TYPE_VIDEO,
+    CODEC_ID_TIFF,
+    sizeof(TiffEncoderContext),
+    tiff_encoder_init,
+    tiff_encode_frame,
+    tiff_encoder_end,
+    NULL,
+    0,
+    NULL,
+    .pix_fmts = (enum PixelFormat[]) {
+                                      PIX_FMT_RGB24,
+                                      PIX_FMT_GRAY8,
+                                      PIX_FMT_MONOWHITE,
+                                      PIX_FMT_MONOBLACK,
+                                      PIX_FMT_PAL8,
+                                      -1},
+};
Index: libavformat/raw.c
===================================================================
--- libavformat/raw.c	(wersja 8542)
+++ libavformat/raw.c	(kopia robocza)
@@ -43,6 +43,24 @@
     return 0;
 }

+static int tiff_write_header(struct AVFormatContext *s)
+{
+    static const uint8_t header[4] = {0x49, 0x49, 42, 0};
+    AVStream *st = s->streams[0];
+
+    st->codec->extradata = av_malloc(sizeof(int));
+    if (!st->codec->extradata){
+        av_log(NULL, AV_LOG_ERROR, "not enough memory\n");
+        return -1;
+    }
+    st->codec->extradata_size = sizeof(int);
+    *((int*)st->codec->extradata) = 4;
+
+    put_buffer(&s->pb, header, 4);
+
+    return 0;
+}
+
 static int raw_write_packet(struct AVFormatContext *s, AVPacket *pkt)
 {
     put_buffer(&s->pb, pkt->data, pkt->size);
@@ -54,6 +72,18 @@
 {
     return 0;
 }
+
+static int tiff_write_trailer(struct AVFormatContext *s)
+{
+    static const uint8_t trailer[4] = {0, 0, 0, 0};
+    AVStream *st = s->streams[0];
+
+    put_buffer(&s->pb, trailer, 4);
+
+    av_free(st->codec->extradata);
+    return 0;
+}
+
 #endif //CONFIG_MUXERS

 /* raw input */
@@ -873,6 +903,20 @@
     raw_write_trailer,
     .flags= AVFMT_NOTIMESTAMPS,
 };
+
+AVOutputFormat tiff_muxer = {
+    "tiff",
+    "tiff video format",
+    "image/tiff",
+    "tiff,tif",
+    0,
+    CODEC_ID_NONE,
+    CODEC_ID_TIFF,
+    tiff_write_header,
+    raw_write_packet,
+    tiff_write_trailer,
+    .flags = AVFMT_NOTIMESTAMPS,
+};
 #endif //CONFIG_MUXERS

 #ifdef CONFIG_MUXERS
Index: libavformat/allformats.c
===================================================================
--- libavformat/allformats.c	(wersja 8542)
+++ libavformat/allformats.c	(kopia robocza)
@@ -143,6 +143,7 @@
     REGISTER_MUXER   (TG2, tg2);
     REGISTER_MUXER   (TGP, tgp);
     REGISTER_DEMUXER (TIERTEXSEQ, tiertexseq);
+    REGISTER_MUXER   (TIFF, tiff);
     REGISTER_DEMUXER (TTA, tta);
     REGISTER_DEMUXER (V4L2, v4l2);
     REGISTER_DEMUXER (VC1, vc1);
Index: libavformat/allformats.h
===================================================================
--- libavformat/allformats.h	(wersja 8542)
+++ libavformat/allformats.h	(kopia robocza)
@@ -153,6 +153,7 @@
 extern AVInputFormat sol_demuxer;
 extern AVInputFormat swf_demuxer;
 extern AVOutputFormat swf_muxer;
+extern AVOutputFormat tiff_muxer;
 extern AVInputFormat tta_demuxer;
 extern AVInputFormat v4l2_demuxer;
 extern AVInputFormat vc1_demuxer;



More information about the ffmpeg-devel mailing list