diff options
Diffstat (limited to 'libavcodec/imgconvert.c')
-rw-r--r-- | libavcodec/imgconvert.c | 295 |
1 files changed, 214 insertions, 81 deletions
diff --git a/libavcodec/imgconvert.c b/libavcodec/imgconvert.c index ff4236e649..09d24899f1 100644 --- a/libavcodec/imgconvert.c +++ b/libavcodec/imgconvert.c @@ -2,20 +2,20 @@ * Misc image conversion routines * Copyright (c) 2001, 2002, 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * 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. * - * Libav is distributed in the hope that it will be useful, + * 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,6 +33,7 @@ #include "avcodec.h" #include "dsputil.h" #include "internal.h" +#include "libavutil/avassert.h" #include "libavutil/colorspace.h" #include "libavutil/common.h" #include "libavutil/pixdesc.h" @@ -42,6 +43,12 @@ #include "x86/dsputil_mmx.h" #endif +#define FF_COLOR_NA -1 +#define FF_COLOR_RGB 0 /**< RGB color space */ +#define FF_COLOR_GRAY 1 /**< gray color space */ +#define FF_COLOR_YUV 2 /**< YUV color space. 16 <= Y <= 235, 16 <= U, V <= 240 */ +#define FF_COLOR_YUV_JPEG 3 /**< YUV color space. 0 <= Y <= 255, 0 <= U, V <= 255 */ + #if HAVE_MMX_EXTERNAL #define deinterlace_line_inplace ff_deinterlace_line_inplace_mmx #define deinterlace_line ff_deinterlace_line_mmx @@ -50,16 +57,50 @@ #define deinterlace_line deinterlace_line_c #endif +#define pixdesc_has_alpha(pixdesc) \ + ((pixdesc)->nb_components == 2 || (pixdesc)->nb_components == 4 || (pixdesc)->flags & PIX_FMT_PAL) + + void avcodec_get_chroma_sub_sample(enum AVPixelFormat pix_fmt, int *h_shift, int *v_shift) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); + av_assert0(desc); *h_shift = desc->log2_chroma_w; *v_shift = desc->log2_chroma_h; } -static int is_gray(const AVPixFmtDescriptor *desc) +static int get_color_type(const AVPixFmtDescriptor *desc) { + if(desc->nb_components == 1 || desc->nb_components == 2) + return FF_COLOR_GRAY; + + if(desc->name && !strncmp(desc->name, "yuvj", 4)) + return FF_COLOR_YUV_JPEG; + + if(desc->flags & PIX_FMT_RGB) + return FF_COLOR_RGB; + + if(desc->nb_components == 0) + return FF_COLOR_NA; + + return FF_COLOR_YUV; +} + +static int get_pix_fmt_depth(int *min, int *max, enum AVPixelFormat pix_fmt) { - return desc->nb_components - (desc->flags & PIX_FMT_ALPHA) == 1; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); + int i; + + if (!desc || !desc->nb_components) { + *min = *max = 0; + return AVERROR(EINVAL); + } + + *min = INT_MAX, *max = -INT_MAX; + for (i = 0; i < desc->nb_components; i++) { + *min = FFMIN(desc->comp[i].depth_minus1+1, *min); + *max = FFMAX(desc->comp[i].depth_minus1+1, *max); + } + return 0; } int avcodec_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, @@ -68,8 +109,12 @@ int avcodec_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, { const AVPixFmtDescriptor *src_desc = av_pix_fmt_desc_get(src_pix_fmt); const AVPixFmtDescriptor *dst_desc = av_pix_fmt_desc_get(dst_pix_fmt); - int loss, i, nb_components = FFMIN(src_desc->nb_components, - dst_desc->nb_components); + int src_color, dst_color; + int src_min_depth, src_max_depth, dst_min_depth, dst_max_depth; + int ret, loss, i, nb_components; + + if (dst_pix_fmt >= AV_PIX_FMT_NB || dst_pix_fmt <= AV_PIX_FMT_NONE) + return ~0; /* compute loss */ loss = 0; @@ -77,6 +122,15 @@ int avcodec_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, if (dst_pix_fmt == src_pix_fmt) return 0; + if ((ret = get_pix_fmt_depth(&src_min_depth, &src_max_depth, src_pix_fmt)) < 0) + return ret; + if ((ret = get_pix_fmt_depth(&dst_min_depth, &dst_max_depth, dst_pix_fmt)) < 0) + return ret; + + src_color = get_color_type(src_desc); + dst_color = get_color_type(dst_desc); + nb_components = FFMIN(src_desc->nb_components, dst_desc->nb_components); + for (i = 0; i < nb_components; i++) if (src_desc->comp[i].depth_minus1 > dst_desc->comp[i].depth_minus1) loss |= FF_LOSS_DEPTH; @@ -85,109 +139,138 @@ int avcodec_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, dst_desc->log2_chroma_h > src_desc->log2_chroma_h) loss |= FF_LOSS_RESOLUTION; - if ((src_desc->flags & PIX_FMT_RGB) != (dst_desc->flags & PIX_FMT_RGB)) - loss |= FF_LOSS_COLORSPACE; - - if (has_alpha && !(dst_desc->flags & PIX_FMT_ALPHA) && - (dst_desc->flags & PIX_FMT_ALPHA)) + switch(dst_color) { + case FF_COLOR_RGB: + if (src_color != FF_COLOR_RGB && + src_color != FF_COLOR_GRAY) + loss |= FF_LOSS_COLORSPACE; + break; + case FF_COLOR_GRAY: + if (src_color != FF_COLOR_GRAY) + loss |= FF_LOSS_COLORSPACE; + break; + case FF_COLOR_YUV: + if (src_color != FF_COLOR_YUV) + loss |= FF_LOSS_COLORSPACE; + break; + case FF_COLOR_YUV_JPEG: + if (src_color != FF_COLOR_YUV_JPEG && + src_color != FF_COLOR_YUV && + src_color != FF_COLOR_GRAY) + loss |= FF_LOSS_COLORSPACE; + break; + default: + /* fail safe test */ + if (src_color != dst_color) + loss |= FF_LOSS_COLORSPACE; + break; + } + if (dst_color == FF_COLOR_GRAY && + src_color != FF_COLOR_GRAY) + loss |= FF_LOSS_CHROMA; + if (!pixdesc_has_alpha(dst_desc) && (pixdesc_has_alpha(src_desc) && has_alpha)) loss |= FF_LOSS_ALPHA; - - if (dst_pix_fmt == AV_PIX_FMT_PAL8 && !is_gray(src_desc)) - return loss | FF_LOSS_COLORQUANT; - - if (src_desc->nb_components > dst_desc->nb_components) - if (is_gray(dst_desc)) - loss |= FF_LOSS_CHROMA; + if (dst_pix_fmt == AV_PIX_FMT_PAL8 && + (src_pix_fmt != AV_PIX_FMT_PAL8 && (src_color != FF_COLOR_GRAY || (pixdesc_has_alpha(src_desc) && has_alpha)))) + loss |= FF_LOSS_COLORQUANT; return loss; } -static enum AVPixelFormat avcodec_find_best_pix_fmt1(enum AVPixelFormat *pix_fmt_list, - enum AVPixelFormat src_pix_fmt, - int has_alpha, - int loss_mask) -{ - int dist, i, loss, min_dist; - enum AVPixelFormat dst_pix_fmt; - - /* find exact color match with smallest size */ - dst_pix_fmt = AV_PIX_FMT_NONE; - min_dist = 0x7fffffff; - i = 0; - while (pix_fmt_list[i] != AV_PIX_FMT_NONE) { - enum AVPixelFormat pix_fmt = pix_fmt_list[i]; - - if (i > AV_PIX_FMT_NB) { - av_log(NULL, AV_LOG_ERROR, "Pixel format list longer than expected, " - "it is either not properly terminated or contains duplicates\n"); - return AV_PIX_FMT_NONE; - } - - loss = avcodec_get_pix_fmt_loss(pix_fmt, src_pix_fmt, has_alpha) & loss_mask; - if (loss == 0) { - dist = av_get_bits_per_pixel(av_pix_fmt_desc_get(pix_fmt)); - if (dist < min_dist) { - min_dist = dist; - dst_pix_fmt = pix_fmt; - } - } - i++; - } - return dst_pix_fmt; -} - #if FF_API_FIND_BEST_PIX_FMT enum AVPixelFormat avcodec_find_best_pix_fmt(int64_t pix_fmt_mask, enum AVPixelFormat src_pix_fmt, - int has_alpha, int *loss_ptr) + int has_alpha, int *loss_ptr) { - enum AVPixelFormat list[64]; - int i, j = 0; + enum AVPixelFormat dst_pix_fmt; + int i; - // test only the first 64 pixel formats to avoid undefined behaviour - for (i = 0; i < 64; i++) { + if (loss_ptr) /* all losses count (for backward compatibility) */ + *loss_ptr = 0; + + dst_pix_fmt = AV_PIX_FMT_NONE; /* so first iteration doesn't have to be treated special */ + for(i = 0; i< FFMIN(AV_PIX_FMT_NB, 64); i++){ if (pix_fmt_mask & (1ULL << i)) - list[j++] = i; + dst_pix_fmt = avcodec_find_best_pix_fmt_of_2(dst_pix_fmt, i, src_pix_fmt, has_alpha, loss_ptr); } - list[j] = AV_PIX_FMT_NONE; - - return avcodec_find_best_pix_fmt2(list, src_pix_fmt, has_alpha, loss_ptr); + return dst_pix_fmt; } #endif /* FF_API_FIND_BEST_PIX_FMT */ -enum AVPixelFormat avcodec_find_best_pix_fmt2(enum AVPixelFormat *pix_fmt_list, - enum AVPixelFormat src_pix_fmt, - int has_alpha, int *loss_ptr) +enum AVPixelFormat avcodec_find_best_pix_fmt_of_2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, + enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr) { enum AVPixelFormat dst_pix_fmt; - int loss_mask, i; + int loss1, loss2, loss_order1, loss_order2, i, loss_mask; + const AVPixFmtDescriptor *desc1 = av_pix_fmt_desc_get(dst_pix_fmt1); + const AVPixFmtDescriptor *desc2 = av_pix_fmt_desc_get(dst_pix_fmt2); static const int loss_mask_order[] = { ~0, /* no loss first */ ~FF_LOSS_ALPHA, ~FF_LOSS_RESOLUTION, + ~FF_LOSS_COLORSPACE, ~(FF_LOSS_COLORSPACE | FF_LOSS_RESOLUTION), ~FF_LOSS_COLORQUANT, ~FF_LOSS_DEPTH, + ~(FF_LOSS_DEPTH|FF_LOSS_COLORSPACE), + ~(FF_LOSS_RESOLUTION | FF_LOSS_DEPTH | FF_LOSS_COLORSPACE | FF_LOSS_ALPHA | + FF_LOSS_COLORQUANT | FF_LOSS_CHROMA), + 0x80000, //non zero entry that combines all loss variants including future additions 0, }; + loss_mask= loss_ptr?~*loss_ptr:~0; /* use loss mask if provided */ + dst_pix_fmt = AV_PIX_FMT_NONE; + loss1 = avcodec_get_pix_fmt_loss(dst_pix_fmt1, src_pix_fmt, has_alpha) & loss_mask; + loss2 = avcodec_get_pix_fmt_loss(dst_pix_fmt2, src_pix_fmt, has_alpha) & loss_mask; + /* try with successive loss */ - i = 0; - for(;;) { - loss_mask = loss_mask_order[i++]; - dst_pix_fmt = avcodec_find_best_pix_fmt1(pix_fmt_list, src_pix_fmt, - has_alpha, loss_mask); - if (dst_pix_fmt >= 0) - goto found; - if (loss_mask == 0) - break; + for(i = 0;loss_mask_order[i] != 0 && dst_pix_fmt == AV_PIX_FMT_NONE;i++) { + loss_order1 = loss1 & loss_mask_order[i]; + loss_order2 = loss2 & loss_mask_order[i]; + + if (loss_order1 == 0 && loss_order2 == 0 && dst_pix_fmt2 != AV_PIX_FMT_NONE && dst_pix_fmt1 != AV_PIX_FMT_NONE){ /* use format with smallest depth */ + if(av_get_padded_bits_per_pixel(desc2) != av_get_padded_bits_per_pixel(desc1)) { + dst_pix_fmt = av_get_padded_bits_per_pixel(desc2) < av_get_padded_bits_per_pixel(desc1) ? dst_pix_fmt2 : dst_pix_fmt1; + } else { + dst_pix_fmt = desc2->nb_components < desc1->nb_components ? dst_pix_fmt2 : dst_pix_fmt1; + } + } else if (loss_order1 == 0 || loss_order2 == 0) { /* use format with no loss */ + dst_pix_fmt = loss_order2 ? dst_pix_fmt1 : dst_pix_fmt2; + } } - return AV_PIX_FMT_NONE; - found: + if (loss_ptr) *loss_ptr = avcodec_get_pix_fmt_loss(dst_pix_fmt, src_pix_fmt, has_alpha); return dst_pix_fmt; } +#if AV_HAVE_INCOMPATIBLE_FORK_ABI +enum AVPixelFormat avcodec_find_best_pix_fmt2(enum AVPixelFormat *pix_fmt_list, + enum AVPixelFormat src_pix_fmt, + int has_alpha, int *loss_ptr){ + return avcodec_find_best_pix_fmt_of_list(pix_fmt_list, src_pix_fmt, has_alpha, loss_ptr); +} +#else +enum AVPixelFormat avcodec_find_best_pix_fmt2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, + enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr) +{ + return avcodec_find_best_pix_fmt_of_2(dst_pix_fmt1, dst_pix_fmt2, src_pix_fmt, has_alpha, loss_ptr); +} +#endif + +enum AVPixelFormat avcodec_find_best_pix_fmt_of_list(enum AVPixelFormat *pix_fmt_list, + enum AVPixelFormat src_pix_fmt, + int has_alpha, int *loss_ptr){ + int i; + + enum AVPixelFormat best = AV_PIX_FMT_NONE; + + for(i=0; pix_fmt_list[i] != AV_PIX_FMT_NONE; i++) + best = avcodec_find_best_pix_fmt_of_2(best, pix_fmt_list[i], src_pix_fmt, has_alpha, loss_ptr); + + return best; +} + /* 2x2 -> 1x1 */ void ff_shrink22(uint8_t *dst, int dst_wrap, const uint8_t *src, int src_wrap, @@ -277,8 +360,22 @@ void ff_shrink88(uint8_t *dst, int dst_wrap, /* return true if yuv planar */ static inline int is_yuv_planar(const AVPixFmtDescriptor *desc) { - return (!(desc->flags & PIX_FMT_RGB) && - (desc->flags & PIX_FMT_PLANAR)); + int i; + int planes[4] = { 0 }; + + if ( desc->flags & PIX_FMT_RGB + || !(desc->flags & PIX_FMT_PLANAR)) + return 0; + + /* set the used planes */ + for (i = 0; i < desc->nb_components; i++) + planes[desc->comp[i].plane] = 1; + + /* if there is an unused plane, the format is not planar */ + for (i = 0; i < desc->nb_components; i++) + if (!planes[i]) + return 0; + return 1; } int av_picture_crop(AVPicture *dst, const AVPicture *src, @@ -288,15 +385,23 @@ int av_picture_crop(AVPicture *dst, const AVPicture *src, int y_shift; int x_shift; - if (pix_fmt < 0 || pix_fmt >= AV_PIX_FMT_NB || !is_yuv_planar(desc)) + if (pix_fmt < 0 || pix_fmt >= AV_PIX_FMT_NB) return -1; y_shift = desc->log2_chroma_h; x_shift = desc->log2_chroma_w; + if (is_yuv_planar(desc)) { dst->data[0] = src->data[0] + (top_band * src->linesize[0]) + left_band; dst->data[1] = src->data[1] + ((top_band >> y_shift) * src->linesize[1]) + (left_band >> x_shift); dst->data[2] = src->data[2] + ((top_band >> y_shift) * src->linesize[2]) + (left_band >> x_shift); + } else{ + if(top_band % (1<<y_shift) || left_band % (1<<x_shift)) + return -1; + if(left_band) //FIXME add support for this too + return -1; + dst->data[0] = src->data[0] + (top_band * src->linesize[0]) + left_band; + } dst->linesize[0] = src->linesize[0]; dst->linesize[1] = src->linesize[1]; @@ -523,3 +628,31 @@ int avpicture_deinterlace(AVPicture *dst, const AVPicture *src, emms_c(); return 0; } + +#ifdef TEST + +int main(void){ + int i; + int err=0; + int skip = 0; + + for (i=0; i<AV_PIX_FMT_NB*2; i++) { + AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(i); + if(!desc || !desc->name) { + skip ++; + continue; + } + if (skip) { + av_log(NULL, AV_LOG_INFO, "%3d unused pixel format values\n", skip); + skip = 0; + } + av_log(NULL, AV_LOG_INFO, "pix fmt %s yuv_plan:%d avg_bpp:%d colortype:%d\n", desc->name, is_yuv_planar(desc), av_get_padded_bits_per_pixel(desc), get_color_type(desc)); + if ((!(desc->flags & PIX_FMT_ALPHA)) != (desc->nb_components != 2 && desc->nb_components != 4)) { + av_log(NULL, AV_LOG_ERROR, "Alpha flag mismatch\n"); + err = 1; + } + } + return err; +} + +#endif |