mirror of
https://github.com/xenia-project/FFmpeg.git
synced 2024-11-24 03:59:43 +00:00
avcodec/tiff: add initial bayer and sub image support
This commit is contained in:
parent
0f2cfa3d80
commit
26772b789b
@ -37,6 +37,7 @@
|
||||
#include "libavutil/avstring.h"
|
||||
#include "libavutil/intreadwrite.h"
|
||||
#include "libavutil/imgutils.h"
|
||||
#include "libavutil/opt.h"
|
||||
#include "avcodec.h"
|
||||
#include "bytestream.h"
|
||||
#include "faxcompr.h"
|
||||
@ -46,11 +47,15 @@
|
||||
#include "tiff.h"
|
||||
#include "tiff_data.h"
|
||||
#include "thread.h"
|
||||
#include "get_bits.h"
|
||||
|
||||
typedef struct TiffContext {
|
||||
AVClass *class;
|
||||
AVCodecContext *avctx;
|
||||
GetByteContext gb;
|
||||
|
||||
int get_subimage;
|
||||
|
||||
int width, height;
|
||||
unsigned int bpp, bppcount;
|
||||
uint32_t palette[256];
|
||||
@ -65,6 +70,12 @@ typedef struct TiffContext {
|
||||
int fill_order;
|
||||
uint32_t res[4];
|
||||
|
||||
int is_bayer;
|
||||
uint8_t pattern[4];
|
||||
unsigned white_level;
|
||||
|
||||
uint32_t sub_ifd;
|
||||
|
||||
int strips, rps, sstype;
|
||||
int sot;
|
||||
int stripsizesoff, stripsize, stripoff, strippos;
|
||||
@ -236,7 +247,8 @@ static int add_metadata(int count, int type,
|
||||
};
|
||||
}
|
||||
|
||||
static void av_always_inline horizontal_fill(unsigned int bpp, uint8_t* dst,
|
||||
static void av_always_inline horizontal_fill(TiffContext *s,
|
||||
unsigned int bpp, uint8_t* dst,
|
||||
int usePtr, const uint8_t *src,
|
||||
uint8_t c, int width, int offset)
|
||||
{
|
||||
@ -267,6 +279,15 @@ static void av_always_inline horizontal_fill(unsigned int bpp, uint8_t* dst,
|
||||
dst[(width+offset)*2+0] = (usePtr ? src[width] : c) >> 4;
|
||||
}
|
||||
break;
|
||||
case 12: {
|
||||
uint16_t *dst16 = (uint16_t *)dst;
|
||||
GetBitContext gb;
|
||||
init_get_bits8(&gb, src, width);
|
||||
for (int i = 0; i < s->width; i++) {
|
||||
dst16[i] = get_bits(&gb, 12) << 4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (usePtr) {
|
||||
memcpy(dst + offset, src, width);
|
||||
@ -368,7 +389,7 @@ static int tiff_unpack_zlib(TiffContext *s, AVFrame *p, uint8_t *dst, int stride
|
||||
src = zbuf;
|
||||
for (line = 0; line < lines; line++) {
|
||||
if (s->bpp < 8 && s->avctx->pix_fmt == AV_PIX_FMT_PAL8) {
|
||||
horizontal_fill(s->bpp, dst, 1, src, 0, width, 0);
|
||||
horizontal_fill(s, s->bpp, dst, 1, src, 0, width, 0);
|
||||
} else {
|
||||
memcpy(dst, src, width);
|
||||
}
|
||||
@ -433,7 +454,7 @@ static int tiff_unpack_lzma(TiffContext *s, AVFrame *p, uint8_t *dst, int stride
|
||||
src = buf;
|
||||
for (line = 0; line < lines; line++) {
|
||||
if (s->bpp < 8 && s->avctx->pix_fmt == AV_PIX_FMT_PAL8) {
|
||||
horizontal_fill(s->bpp, dst, 1, src, 0, width, 0);
|
||||
horizontal_fill(s, s->bpp, dst, 1, src, 0, width, 0);
|
||||
} else {
|
||||
memcpy(dst, src, width);
|
||||
}
|
||||
@ -476,7 +497,7 @@ static int tiff_unpack_fax(TiffContext *s, uint8_t *dst, int stride,
|
||||
s->compr, s->fax_opts);
|
||||
if (s->bpp < 8 && s->avctx->pix_fmt == AV_PIX_FMT_PAL8)
|
||||
for (line = 0; line < lines; line++) {
|
||||
horizontal_fill(s->bpp, dst, 1, dst, 0, width, 0);
|
||||
horizontal_fill(s, s->bpp, dst, 1, dst, 0, width, 0);
|
||||
dst += stride;
|
||||
}
|
||||
return ret;
|
||||
@ -516,6 +537,9 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid
|
||||
av_assert0(width <= bytes_per_row);
|
||||
av_assert0(s->bpp == 24);
|
||||
}
|
||||
if (s->is_bayer) {
|
||||
width = (s->bpp * s->width + 7) >> 3;
|
||||
}
|
||||
|
||||
if (s->compr == TIFF_DEFLATE || s->compr == TIFF_ADOBE_DEFLATE) {
|
||||
#if CONFIG_ZLIB
|
||||
@ -559,7 +583,7 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
if (s->bpp < 8 && s->avctx->pix_fmt == AV_PIX_FMT_PAL8)
|
||||
horizontal_fill(s->bpp, dst, 1, dst, 0, width, 0);
|
||||
horizontal_fill(s, s->bpp, dst, 1, dst, 0, width, 0);
|
||||
if (is_yuv) {
|
||||
unpack_yuv(s, p, dst, strip_start + line);
|
||||
line += s->subsampling[1] - 1;
|
||||
@ -595,7 +619,7 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid
|
||||
return AVERROR_INVALIDDATA;
|
||||
|
||||
if (!s->fill_order) {
|
||||
horizontal_fill(s->bpp * (s->avctx->pix_fmt == AV_PIX_FMT_PAL8),
|
||||
horizontal_fill(s, s->bpp * (s->avctx->pix_fmt == AV_PIX_FMT_PAL8 || s->is_bayer),
|
||||
dst, 1, src, 0, width, 0);
|
||||
} else {
|
||||
int i;
|
||||
@ -619,7 +643,7 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid
|
||||
"Copy went out of bounds\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
horizontal_fill(s->bpp * (s->avctx->pix_fmt == AV_PIX_FMT_PAL8),
|
||||
horizontal_fill(s, s->bpp * (s->avctx->pix_fmt == AV_PIX_FMT_PAL8),
|
||||
dst, 1, src, 0, code, pixels);
|
||||
src += code;
|
||||
pixels += code;
|
||||
@ -631,7 +655,7 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
c = *src++;
|
||||
horizontal_fill(s->bpp * (s->avctx->pix_fmt == AV_PIX_FMT_PAL8),
|
||||
horizontal_fill(s, s->bpp * (s->avctx->pix_fmt == AV_PIX_FMT_PAL8),
|
||||
dst, 0, NULL, c, code, pixels);
|
||||
pixels += code;
|
||||
}
|
||||
@ -665,7 +689,7 @@ static int init_image(TiffContext *s, ThreadFrame *frame)
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
switch (s->planar * 1000 + s->bpp * 10 + s->bppcount) {
|
||||
switch (s->planar * 1000 + s->bpp * 10 + s->bppcount + s->is_bayer * 10000) {
|
||||
case 11:
|
||||
if (!s->palette_is_set) {
|
||||
s->avctx->pix_fmt = AV_PIX_FMT_MONOBLACK;
|
||||
@ -681,6 +705,66 @@ static int init_image(TiffContext *s, ThreadFrame *frame)
|
||||
case 81:
|
||||
s->avctx->pix_fmt = s->palette_is_set ? AV_PIX_FMT_PAL8 : AV_PIX_FMT_GRAY8;
|
||||
break;
|
||||
case 10081:
|
||||
switch (s->pattern[0] | (s->pattern[1] << 8) | (s->pattern[2] << 16) | (s->pattern[3] << 24)) {
|
||||
case 0x02010100:
|
||||
s->avctx->pix_fmt = AV_PIX_FMT_BAYER_RGGB8;
|
||||
break;
|
||||
case 0x00010102:
|
||||
s->avctx->pix_fmt = AV_PIX_FMT_BAYER_BGGR8;
|
||||
break;
|
||||
case 0x01000201:
|
||||
s->avctx->pix_fmt = AV_PIX_FMT_BAYER_GBRG8;
|
||||
break;
|
||||
case 0x01020001:
|
||||
s->avctx->pix_fmt = AV_PIX_FMT_BAYER_GRBG8;
|
||||
break;
|
||||
default:
|
||||
av_log(s->avctx, AV_LOG_ERROR, "Unsupported Bayer pattern: 0x%X\n",
|
||||
s->pattern[0] | s->pattern[1] << 8 | s->pattern[2] << 16 | s->pattern[3] << 24);
|
||||
return AVERROR_PATCHWELCOME;
|
||||
}
|
||||
break;
|
||||
case 10121:
|
||||
switch (s->pattern[0] | s->pattern[1] << 8 | s->pattern[2] << 16 | s->pattern[3] << 24) {
|
||||
case 0x02010100:
|
||||
s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_RGGB16LE : AV_PIX_FMT_BAYER_RGGB16BE;
|
||||
break;
|
||||
case 0x00010102:
|
||||
s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_BGGR16LE : AV_PIX_FMT_BAYER_BGGR16BE;
|
||||
break;
|
||||
case 0x01000201:
|
||||
s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_GBRG16LE : AV_PIX_FMT_BAYER_GBRG16BE;
|
||||
break;
|
||||
case 0x01020001:
|
||||
s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_GRBG16LE : AV_PIX_FMT_BAYER_GRBG16BE;
|
||||
break;
|
||||
default:
|
||||
av_log(s->avctx, AV_LOG_ERROR, "Unsupported Bayer pattern: 0x%X\n",
|
||||
s->pattern[0] | s->pattern[1] << 8 | s->pattern[2] << 16 | s->pattern[3] << 24);
|
||||
return AVERROR_PATCHWELCOME;
|
||||
}
|
||||
break;
|
||||
case 10161:
|
||||
switch (s->pattern[0] | s->pattern[1] << 8 | s->pattern[2] << 16 | s->pattern[3] << 24) {
|
||||
case 0x02010100:
|
||||
s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_RGGB16LE : AV_PIX_FMT_BAYER_RGGB16BE;
|
||||
break;
|
||||
case 0x00010102:
|
||||
s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_BGGR16LE : AV_PIX_FMT_BAYER_BGGR16BE;
|
||||
break;
|
||||
case 0x01000201:
|
||||
s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_GBRG16LE : AV_PIX_FMT_BAYER_GBRG16BE;
|
||||
break;
|
||||
case 0x01020001:
|
||||
s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_GRBG16LE : AV_PIX_FMT_BAYER_GRBG16BE;
|
||||
break;
|
||||
default:
|
||||
av_log(s->avctx, AV_LOG_ERROR, "Unsupported Bayer pattern: 0x%X\n",
|
||||
s->pattern[0] | s->pattern[1] << 8 | s->pattern[2] << 16 | s->pattern[3] << 24);
|
||||
return AVERROR_PATCHWELCOME;
|
||||
}
|
||||
break;
|
||||
case 243:
|
||||
if (s->photometric == TIFF_PHOTOMETRIC_YCBCR) {
|
||||
if (s->subsampling[0] == 1 && s->subsampling[1] == 1) {
|
||||
@ -961,6 +1045,26 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
|
||||
case TIFF_PREDICTOR:
|
||||
s->predictor = value;
|
||||
break;
|
||||
case TIFF_SUB_IFDS:
|
||||
s->sub_ifd = value;
|
||||
break;
|
||||
case TIFF_WHITE_LEVEL:
|
||||
s->white_level = value;
|
||||
break;
|
||||
case TIFF_CFA_PATTERN_DIM:
|
||||
if (count != 2 || (ff_tget(&s->gb, type, s->le) != 2 &&
|
||||
ff_tget(&s->gb, type, s->le) != 2)) {
|
||||
av_log(s->avctx, AV_LOG_ERROR, "CFA Pattern dimensions are not 2x2\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
break;
|
||||
case TIFF_CFA_PATTERN:
|
||||
s->is_bayer = 1;
|
||||
s->pattern[0] = ff_tget(&s->gb, type, s->le);
|
||||
s->pattern[1] = ff_tget(&s->gb, type, s->le);
|
||||
s->pattern[2] = ff_tget(&s->gb, type, s->le);
|
||||
s->pattern[3] = ff_tget(&s->gb, type, s->le);
|
||||
break;
|
||||
case TIFF_PHOTOMETRIC:
|
||||
switch (value) {
|
||||
case TIFF_PHOTOMETRIC_WHITE_IS_ZERO:
|
||||
@ -968,6 +1072,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
|
||||
case TIFF_PHOTOMETRIC_RGB:
|
||||
case TIFF_PHOTOMETRIC_PALETTE:
|
||||
case TIFF_PHOTOMETRIC_YCBCR:
|
||||
case TIFF_PHOTOMETRIC_CFA:
|
||||
s->photometric = value;
|
||||
break;
|
||||
case TIFF_PHOTOMETRIC_ALPHA_MASK:
|
||||
@ -975,7 +1080,6 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
|
||||
case TIFF_PHOTOMETRIC_CIE_LAB:
|
||||
case TIFF_PHOTOMETRIC_ICC_LAB:
|
||||
case TIFF_PHOTOMETRIC_ITU_LAB:
|
||||
case TIFF_PHOTOMETRIC_CFA:
|
||||
case TIFF_PHOTOMETRIC_LOG_L:
|
||||
case TIFF_PHOTOMETRIC_LOG_LUV:
|
||||
case TIFF_PHOTOMETRIC_LINEAR_RAW:
|
||||
@ -1192,7 +1296,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
|
||||
default:
|
||||
if (s->avctx->err_recognition & AV_EF_EXPLODE) {
|
||||
av_log(s->avctx, AV_LOG_ERROR,
|
||||
"Unknown or unsupported tag %d/0X%0X\n",
|
||||
"Unknown or unsupported tag %d/0x%0X\n",
|
||||
tag, tag);
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
@ -1235,10 +1339,13 @@ static int decode_frame(AVCodecContext *avctx,
|
||||
}
|
||||
s->le = le;
|
||||
// TIFF_BPP is not a required tag and defaults to 1
|
||||
again:
|
||||
s->bppcount = s->bpp = 1;
|
||||
s->photometric = TIFF_PHOTOMETRIC_NONE;
|
||||
s->compr = TIFF_RAW;
|
||||
s->fill_order = 0;
|
||||
s->white_level = 0;
|
||||
s->is_bayer = 0;
|
||||
free_geotags(s);
|
||||
|
||||
// Reset these offsets so we can tell if they were set this frame
|
||||
@ -1253,6 +1360,16 @@ static int decode_frame(AVCodecContext *avctx,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (s->sub_ifd && s->get_subimage) {
|
||||
off = s->sub_ifd;
|
||||
if (off >= UINT_MAX - 14 || avpkt->size < off + 14) {
|
||||
av_log(avctx, AV_LOG_ERROR, "IFD offset is greater than image size\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
s->sub_ifd = 0;
|
||||
goto again;
|
||||
}
|
||||
|
||||
for (i = 0; i<s->geotag_count; i++) {
|
||||
const char *keyname = get_geokey_name(s->geotags[i].key);
|
||||
if (!keyname) {
|
||||
@ -1390,6 +1507,15 @@ static int decode_frame(AVCodecContext *avctx,
|
||||
FFSWAP(int, p->linesize[0], p->linesize[1]);
|
||||
}
|
||||
|
||||
if (s->is_bayer && s->white_level && s->bpp == 16) {
|
||||
uint16_t *dst = (uint16_t *)p->data[0];
|
||||
for (i = 0; i < s->height; i++) {
|
||||
for (j = 0; j < s->width; j++)
|
||||
dst[j] = FFMIN((dst[j] / (float)s->white_level) * 65535, 65535);
|
||||
dst += stride / 2;
|
||||
}
|
||||
}
|
||||
|
||||
*got_frame = 1;
|
||||
|
||||
return avpkt->size;
|
||||
@ -1426,6 +1552,19 @@ static av_cold int tiff_end(AVCodecContext *avctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define OFFSET(x) offsetof(TiffContext, x)
|
||||
static const AVOption tiff_options[] = {
|
||||
{ "subimage", "decode subimage instead if available", OFFSET(get_subimage), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static const AVClass tiff_decoder_class = {
|
||||
.class_name = "TIFF decoder",
|
||||
.item_name = av_default_item_name,
|
||||
.option = tiff_options,
|
||||
.version = LIBAVUTIL_VERSION_INT,
|
||||
};
|
||||
|
||||
AVCodec ff_tiff_decoder = {
|
||||
.name = "tiff",
|
||||
.long_name = NULL_IF_CONFIG_SMALL("TIFF image"),
|
||||
@ -1437,4 +1576,5 @@ AVCodec ff_tiff_decoder = {
|
||||
.decode = decode_frame,
|
||||
.init_thread_copy = ONLY_IF_THREADS_ENABLED(tiff_init),
|
||||
.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS,
|
||||
.priv_class = &tiff_decoder_class,
|
||||
};
|
||||
|
@ -70,18 +70,22 @@ enum TiffTags {
|
||||
TIFF_TILE_LENGTH = 0x143,
|
||||
TIFF_TILE_OFFSETS = 0x144,
|
||||
TIFF_TILE_BYTE_COUNTS = 0x145,
|
||||
TIFF_SUB_IFDS = 0x14A,
|
||||
TIFF_EXTRASAMPLES = 0x152,
|
||||
TIFF_YCBCR_COEFFICIENTS = 0x211,
|
||||
TIFF_YCBCR_SUBSAMPLING = 0x212,
|
||||
TIFF_YCBCR_POSITIONING = 0x213,
|
||||
TIFF_REFERENCE_BW = 0x214,
|
||||
TIFF_CFA_PATTERN_DIM = 0x828D,
|
||||
TIFF_CFA_PATTERN = 0x828E,
|
||||
TIFF_COPYRIGHT = 0x8298,
|
||||
TIFF_MODEL_TIEPOINT = 0x8482,
|
||||
TIFF_MODEL_PIXEL_SCALE = 0x830E,
|
||||
TIFF_MODEL_TRANSFORMATION= 0x8480,
|
||||
TIFF_GEO_KEY_DIRECTORY = 0x87AF,
|
||||
TIFF_GEO_DOUBLE_PARAMS = 0x87B0,
|
||||
TIFF_GEO_ASCII_PARAMS = 0x87B1
|
||||
TIFF_GEO_ASCII_PARAMS = 0x87B1,
|
||||
TIFF_WHITE_LEVEL = 0xC61D,
|
||||
};
|
||||
|
||||
/** list of TIFF compression types */
|
||||
|
Loading…
Reference in New Issue
Block a user