X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fimgresample.c;h=d1d6757c6c23747f495807645b6958865d8fd282;hb=f8a80fd69df1735730f1b3c7f37506ffe0570d0d;hp=26519bd381cdbd936f9a9c70a17d1791875c8e70;hpb=ff4ec49e648bead601b3b8c618f6d19f3548d368;p=ffmpeg diff --git a/libavcodec/imgresample.c b/libavcodec/imgresample.c index 26519bd381c..d1d6757c6c2 100644 --- a/libavcodec/imgresample.c +++ b/libavcodec/imgresample.c @@ -1,35 +1,44 @@ /* - * High quality image resampling with polyphase filters + * High quality image resampling with polyphase filters * Copyright (c) 2001 Fabrice Bellard. * - * This library is free software; you can redistribute it and/or + * 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 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file imgresample.c + * High quality image resampling with polyphase filters . */ + #include "avcodec.h" +#include "swscale.h" #include "dsputil.h" -#ifdef USE_FASTMEMCPY -#include "fastmemcpy.h" +#ifdef HAVE_ALTIVEC +#include "ppc/imgresample_altivec.h" #endif - #define NB_COMPONENTS 3 #define PHASE_BITS 4 #define NB_PHASES (1 << PHASE_BITS) #define NB_TAPS 4 #define FCENTER 1 /* index of the center of the filter */ +//#define TEST 1 /* Test it */ #define POS_FRAC_BITS 16 #define POS_FRAC (1 << POS_FRAC_BITS) @@ -38,26 +47,38 @@ #define LINE_BUF_HEIGHT (NB_TAPS * 4) +struct SwsContext { + AVClass *av_class; + struct ImgReSampleContext *resampling_ctx; + enum PixelFormat src_pix_fmt, dst_pix_fmt; +}; + struct ImgReSampleContext { int iwidth, iheight, owidth, oheight; + int topBand, bottomBand, leftBand, rightBand; + int padtop, padbottom, padleft, padright; + int pad_owidth, pad_oheight; int h_incr, v_incr; - INT16 h_filters[NB_PHASES][NB_TAPS] __align8; /* horizontal filters */ - INT16 v_filters[NB_PHASES][NB_TAPS] __align8; /* vertical filters */ - UINT8 *line_buf; + DECLARE_ALIGNED_8(int16_t, h_filters[NB_PHASES][NB_TAPS]); /* horizontal filters */ + DECLARE_ALIGNED_8(int16_t, v_filters[NB_PHASES][NB_TAPS]); /* vertical filters */ + uint8_t *line_buf; }; +void av_build_filter(int16_t *filter, double factor, int tap_count, int phase_count, int scale, int type); + static inline int get_phase(int pos) { return ((pos) >> (POS_FRAC_BITS - PHASE_BITS)) & ((1 << PHASE_BITS) - 1); } /* This function must be optimized */ -static void h_resample_fast(UINT8 *dst, int dst_width, UINT8 *src, int src_width, - int src_start, int src_incr, INT16 *filters) +static void h_resample_fast(uint8_t *dst, int dst_width, const uint8_t *src, + int src_width, int src_start, int src_incr, + int16_t *filters) { int src_pos, phase, sum, i; - UINT8 *s; - INT16 *filter; + const uint8_t *s; + int16_t *filter; src_pos = src_start; for(i=0;i> POS_FRAC_BITS) < 0 || (src_pos >> POS_FRAC_BITS) > (src_width - NB_TAPS)) - abort(); + av_abort(); #endif s = src + (src_pos >> POS_FRAC_BITS); phase = get_phase(src_pos); @@ -95,11 +116,11 @@ static void h_resample_fast(UINT8 *dst, int dst_width, UINT8 *src, int src_width } /* This function must be optimized */ -static void v_resample(UINT8 *dst, int dst_width, UINT8 *src, int wrap, - INT16 *filter) +static void v_resample(uint8_t *dst, int dst_width, const uint8_t *src, + int wrap, int16_t *filter) { int sum, i; - UINT8 *s; + const uint8_t *s; s = src; for(i=0;i= 4) { @@ -235,11 +257,11 @@ static void v_resample4_mmx(UINT8 *dst, int dst_width, UINT8 *src, int wrap, paddw_r2r(mm3, mm2); paddw_r2r(mm2, mm0); psraw_i2r(FILTER_BITS, mm0); - + packuswb_r2r(mm7, mm0); movq_r2m(mm0, tmp); - *(UINT32 *)dst = tmp.ud[0]; + *(uint32_t *)dst = tmp.ud[0]; dst += 4; s += 4; dst_width -= 4; @@ -261,15 +283,16 @@ static void v_resample4_mmx(UINT8 *dst, int dst_width, UINT8 *src, int wrap, } emms(); } -#endif +#endif /* HAVE_MMX */ /* slow version to handle limit cases. Does not need optimisation */ -static void h_resample_slow(UINT8 *dst, int dst_width, UINT8 *src, int src_width, - int src_start, int src_incr, INT16 *filters) +static void h_resample_slow(uint8_t *dst, int dst_width, + const uint8_t *src, int src_width, + int src_start, int src_incr, int16_t *filters) { int src_pos, phase, sum, j, v, i; - UINT8 *s, *src_end; - INT16 *filter; + const uint8_t *s, *src_end; + int16_t *filter; src_end = src + src_width; src_pos = src_start; @@ -299,8 +322,9 @@ static void h_resample_slow(UINT8 *dst, int dst_width, UINT8 *src, int src_width } } -static void h_resample(UINT8 *dst, int dst_width, UINT8 *src, int src_width, - int src_start, int src_incr, INT16 *filters) +static void h_resample(uint8_t *dst, int dst_width, const uint8_t *src, + int src_width, int src_start, int src_incr, + int16_t *filters) { int n, src_end; @@ -313,38 +337,38 @@ static void h_resample(UINT8 *dst, int dst_width, UINT8 *src, int src_width, } src_end = src_start + dst_width * src_incr; if (src_end > ((src_width - NB_TAPS) << POS_FRAC_BITS)) { - n = (((src_width - NB_TAPS + 1) << POS_FRAC_BITS) - 1 - src_start) / + n = (((src_width - NB_TAPS + 1) << POS_FRAC_BITS) - 1 - src_start) / src_incr; } else { n = dst_width; } #ifdef HAVE_MMX if ((mm_flags & MM_MMX) && NB_TAPS == 4) - h_resample_fast4_mmx(dst, n, + h_resample_fast4_mmx(dst, n, src, src_width, src_start, src_incr, filters); else #endif - h_resample_fast(dst, n, + h_resample_fast(dst, n, src, src_width, src_start, src_incr, filters); if (n < dst_width) { dst += n; dst_width -= n; src_start += n * src_incr; - h_resample_slow(dst, dst_width, + h_resample_slow(dst, dst_width, src, src_width, src_start, src_incr, filters); } } -static void component_resample(ImgReSampleContext *s, - UINT8 *output, int owrap, int owidth, int oheight, - UINT8 *input, int iwrap, int iwidth, int iheight) +static void component_resample(ImgReSampleContext *s, + uint8_t *output, int owrap, int owidth, int oheight, + uint8_t *input, int iwrap, int iwidth, int iheight) { int src_y, src_y1, last_src_y, ring_y, phase_y, y1, y; - UINT8 *new_line, *src_line; + uint8_t *new_line, *src_line; last_src_y = - FCENTER - 1; /* position of the bottom of the filter in the source image */ - src_y = (last_src_y + NB_TAPS) * POS_FRAC; + src_y = (last_src_y + NB_TAPS) * POS_FRAC; ring_y = NB_TAPS; /* position in ring buffer */ for(y=0;y= LINE_BUF_HEIGHT + NB_TAPS) ring_y = NB_TAPS; last_src_y++; - /* handle limit conditions : replicate line (slighly - inefficient because we filter multiple times */ + /* handle limit conditions : replicate line (slightly + inefficient because we filter multiple times) */ y1 = last_src_y; if (y1 < 0) { y1 = 0; @@ -364,8 +388,8 @@ static void component_resample(ImgReSampleContext *s, src_line = input + y1 * iwrap; new_line = s->line_buf + ring_y * owidth; /* apply filter and handle limit cases correctly */ - h_resample(new_line, owidth, - src_line, iwidth, - FCENTER * POS_FRAC, s->h_incr, + h_resample(new_line, owidth, + src_line, iwidth, - FCENTER * POS_FRAC, s->h_incr, &s->h_filters[0][0]); /* handle ring buffer wraping */ if (ring_y >= LINE_BUF_HEIGHT) { @@ -378,93 +402,107 @@ static void component_resample(ImgReSampleContext *s, #ifdef HAVE_MMX /* desactivated MMX because loss of precision */ if ((mm_flags & MM_MMX) && NB_TAPS == 4 && 0) - v_resample4_mmx(output, owidth, - s->line_buf + (ring_y - NB_TAPS + 1) * owidth, owidth, + v_resample4_mmx(output, owidth, + s->line_buf + (ring_y - NB_TAPS + 1) * owidth, owidth, &s->v_filters[phase_y][0]); else #endif - v_resample(output, owidth, - s->line_buf + (ring_y - NB_TAPS + 1) * owidth, owidth, +#ifdef HAVE_ALTIVEC + if ((mm_flags & MM_ALTIVEC) && NB_TAPS == 4 && FILTER_BITS <= 6) + v_resample16_altivec(output, owidth, + s->line_buf + (ring_y - NB_TAPS + 1) * owidth, owidth, + &s->v_filters[phase_y][0]); + else +#endif + v_resample(output, owidth, + s->line_buf + (ring_y - NB_TAPS + 1) * owidth, owidth, &s->v_filters[phase_y][0]); - + src_y += s->v_incr; + output += owrap; } } -/* XXX: the following filter is quite naive, but it seems to suffice - for 4 taps */ -static void build_filter(INT16 *filter, float factor) +ImgReSampleContext *img_resample_init(int owidth, int oheight, + int iwidth, int iheight) { - int ph, i, v; - float x, y, tab[NB_TAPS], norm, mult; - - /* if upsampling, only need to interpolate, no filter */ - if (factor > 1.0) - factor = 1.0; - - for(ph=0;ph= UINT_MAX / (LINE_BUF_HEIGHT + NB_TAPS)) + return NULL; s->line_buf = av_mallocz(owidth * (LINE_BUF_HEIGHT + NB_TAPS)); - if (!s->line_buf) + if (!s->line_buf) goto fail; - + s->owidth = owidth; s->oheight = oheight; s->iwidth = iwidth; s->iheight = iheight; - - s->h_incr = (iwidth * POS_FRAC) / owidth; - s->v_incr = (iheight * POS_FRAC) / oheight; - - build_filter(&s->h_filters[0][0], (float)owidth / (float)iwidth); - build_filter(&s->v_filters[0][0], (float)oheight / (float)iheight); + + s->topBand = topBand; + s->bottomBand = bottomBand; + s->leftBand = leftBand; + s->rightBand = rightBand; + + s->padtop = padtop; + s->padbottom = padbottom; + s->padleft = padleft; + s->padright = padright; + + s->pad_owidth = owidth - (padleft + padright); + s->pad_oheight = oheight - (padtop + padbottom); + + s->h_incr = ((iwidth - leftBand - rightBand) * POS_FRAC) / s->pad_owidth; + s->v_incr = ((iheight - topBand - bottomBand) * POS_FRAC) / s->pad_oheight; + + av_build_filter(&s->h_filters[0][0], (float) s->pad_owidth / + (float) (iwidth - leftBand - rightBand), NB_TAPS, NB_PHASES, 1<v_filters[0][0], (float) s->pad_oheight / + (float) (iheight - topBand - bottomBand), NB_TAPS, NB_PHASES, 1<data[i], output->linesize[i], - s->owidth >> shift, s->oheight >> shift, - input->data[i], input->linesize[i], - s->iwidth >> shift, s->iheight >> shift); + + optr = output->data[i] + (((output->linesize[i] * + s->padtop) + s->padleft) >> shift); + + component_resample(s, optr, output->linesize[i], + s->pad_owidth >> shift, s->pad_oheight >> shift, + input->data[i] + (input->linesize[i] * + (s->topBand >> shift)) + (s->leftBand >> shift), + input->linesize[i], ((s->iwidth - s->leftBand - + s->rightBand) >> shift), + (s->iheight - s->topBand - s->bottomBand) >> shift); } } @@ -474,51 +512,225 @@ void img_resample_close(ImgReSampleContext *s) av_free(s); } -#ifdef TEST +struct SwsContext *sws_getContext(int srcW, int srcH, int srcFormat, + int dstW, int dstH, int dstFormat, + int flags, SwsFilter *srcFilter, + SwsFilter *dstFilter, double *param) +{ + struct SwsContext *ctx; + + ctx = av_malloc(sizeof(struct SwsContext)); + if (ctx) + ctx->av_class = av_mallocz(sizeof(AVClass)); + if (!ctx || !ctx->av_class) { + av_log(NULL, AV_LOG_ERROR, "Cannot allocate a resampling context!\n"); + + return NULL; + } -void *av_mallocz(int size) + if ((srcH != dstH) || (srcW != dstW)) { + if ((srcFormat != PIX_FMT_YUV420P) || (dstFormat != PIX_FMT_YUV420P)) { + av_log(NULL, AV_LOG_INFO, "PIX_FMT_YUV420P will be used as an intermediate format for rescaling\n"); + } + ctx->resampling_ctx = img_resample_init(dstW, dstH, srcW, srcH); + } else { + ctx->resampling_ctx = av_malloc(sizeof(ImgReSampleContext)); + ctx->resampling_ctx->iheight = srcH; + ctx->resampling_ctx->iwidth = srcW; + ctx->resampling_ctx->oheight = dstH; + ctx->resampling_ctx->owidth = dstW; + } + ctx->src_pix_fmt = srcFormat; + ctx->dst_pix_fmt = dstFormat; + + return ctx; +} + +void sws_freeContext(struct SwsContext *ctx) +{ + if (!ctx) + return; + if ((ctx->resampling_ctx->iwidth != ctx->resampling_ctx->owidth) || + (ctx->resampling_ctx->iheight != ctx->resampling_ctx->oheight)) { + img_resample_close(ctx->resampling_ctx); + } else { + av_free(ctx->resampling_ctx); + } + av_free(ctx->av_class); + av_free(ctx); +} + + +/** + * Checks if context is valid or reallocs a new one instead. + * If context is NULL, just calls sws_getContext() to get a new one. + * Otherwise, checks if the parameters are the same already saved in context. + * If that is the case, returns the current context. + * Otherwise, frees context and gets a new one. + * + * Be warned that srcFilter, dstFilter are not checked, they are + * asumed to remain valid. + */ +struct SwsContext *sws_getCachedContext(struct SwsContext *ctx, + int srcW, int srcH, int srcFormat, + int dstW, int dstH, int dstFormat, int flags, + SwsFilter *srcFilter, SwsFilter *dstFilter, double *param) +{ + if (ctx != NULL) { + if ((ctx->resampling_ctx->iwidth != srcW) || + (ctx->resampling_ctx->iheight != srcH) || + (ctx->src_pix_fmt != srcFormat) || + (ctx->resampling_ctx->owidth != dstW) || + (ctx->resampling_ctx->oheight != dstH) || + (ctx->dst_pix_fmt != dstFormat)) + { + sws_freeContext(ctx); + ctx = NULL; + } + } + if (ctx == NULL) { + return sws_getContext(srcW, srcH, srcFormat, + dstW, dstH, dstFormat, flags, + srcFilter, dstFilter, param); + } + return ctx; +} + +int sws_scale(struct SwsContext *ctx, uint8_t* src[], int srcStride[], + int srcSliceY, int srcSliceH, uint8_t* dst[], int dstStride[]) { - void *ptr; - ptr = malloc(size); - memset(ptr, 0, size); - return ptr; + AVPicture src_pict, dst_pict; + int i, res = 0; + AVPicture picture_format_temp; + AVPicture picture_resample_temp, *formatted_picture, *resampled_picture; + uint8_t *buf1 = NULL, *buf2 = NULL; + enum PixelFormat current_pix_fmt; + + for (i = 0; i < 4; i++) { + src_pict.data[i] = src[i]; + src_pict.linesize[i] = srcStride[i]; + dst_pict.data[i] = dst[i]; + dst_pict.linesize[i] = dstStride[i]; + } + if ((ctx->resampling_ctx->iwidth != ctx->resampling_ctx->owidth) || + (ctx->resampling_ctx->iheight != ctx->resampling_ctx->oheight)) { + /* We have to rescale the picture, but only YUV420P rescaling is supported... */ + + if (ctx->src_pix_fmt != PIX_FMT_YUV420P) { + int size; + + /* create temporary picture for rescaling input*/ + size = avpicture_get_size(PIX_FMT_YUV420P, ctx->resampling_ctx->iwidth, ctx->resampling_ctx->iheight); + buf1 = av_malloc(size); + if (!buf1) { + res = -1; + goto the_end; + } + formatted_picture = &picture_format_temp; + avpicture_fill((AVPicture*)formatted_picture, buf1, + PIX_FMT_YUV420P, ctx->resampling_ctx->iwidth, ctx->resampling_ctx->iheight); + + if (img_convert((AVPicture*)formatted_picture, PIX_FMT_YUV420P, + &src_pict, ctx->src_pix_fmt, + ctx->resampling_ctx->iwidth, ctx->resampling_ctx->iheight) < 0) { + + av_log(NULL, AV_LOG_ERROR, "pixel format conversion not handled\n"); + res = -1; + goto the_end; + } + } else { + formatted_picture = &src_pict; + } + + if (ctx->dst_pix_fmt != PIX_FMT_YUV420P) { + int size; + + /* create temporary picture for rescaling output*/ + size = avpicture_get_size(PIX_FMT_YUV420P, ctx->resampling_ctx->owidth, ctx->resampling_ctx->oheight); + buf2 = av_malloc(size); + if (!buf2) { + res = -1; + goto the_end; + } + resampled_picture = &picture_resample_temp; + avpicture_fill((AVPicture*)resampled_picture, buf2, + PIX_FMT_YUV420P, ctx->resampling_ctx->owidth, ctx->resampling_ctx->oheight); + + } else { + resampled_picture = &dst_pict; + } + + /* ...and finally rescale!!! */ + img_resample(ctx->resampling_ctx, resampled_picture, formatted_picture); + current_pix_fmt = PIX_FMT_YUV420P; + } else { + resampled_picture = &src_pict; + current_pix_fmt = ctx->src_pix_fmt; + } + + if (current_pix_fmt != ctx->dst_pix_fmt) { + if (img_convert(&dst_pict, ctx->dst_pix_fmt, + resampled_picture, current_pix_fmt, + ctx->resampling_ctx->owidth, ctx->resampling_ctx->oheight) < 0) { + + av_log(NULL, AV_LOG_ERROR, "pixel format conversion not handled\n"); + + res = -1; + goto the_end; + } + } else if (resampled_picture != &dst_pict) { + av_picture_copy(&dst_pict, resampled_picture, current_pix_fmt, + ctx->resampling_ctx->owidth, ctx->resampling_ctx->oheight); + } + +the_end: + av_free(buf1); + av_free(buf2); + return res; } + +#ifdef TEST +#include +#undef exit + /* input */ #define XSIZE 256 #define YSIZE 256 -UINT8 img[XSIZE * YSIZE]; +uint8_t img[XSIZE * YSIZE]; /* output */ #define XSIZE1 512 #define YSIZE1 512 -UINT8 img1[XSIZE1 * YSIZE1]; -UINT8 img2[XSIZE1 * YSIZE1]; +uint8_t img1[XSIZE1 * YSIZE1]; +uint8_t img2[XSIZE1 * YSIZE1]; -void save_pgm(const char *filename, UINT8 *img, int xsize, int ysize) +void save_pgm(const char *filename, uint8_t *img, int xsize, int ysize) { +#undef fprintf FILE *f; f=fopen(filename,"w"); fprintf(f,"P5\n%d %d\n%d\n", xsize, ysize, 255); fwrite(img,1, xsize * ysize,f); fclose(f); +#define fprintf please_use_av_log } -static void dump_filter(INT16 *filter) +static void dump_filter(int16_t *filter) { int i, ph; for(ph=0;phh_filters[0][0]); component_resample(s, img1, xsize, xsize, ysize, - img, XSIZE, XSIZE, YSIZE); + img + 50 * XSIZE, XSIZE, XSIZE, YSIZE - 100); img_resample_close(s); - sprintf(buf, "/tmp/out%d.pgm", i); + snprintf(buf, sizeof(buf), "/tmp/out%d.pgm", i); save_pgm(buf, img1, xsize, ysize); } /* mmx test */ #ifdef HAVE_MMX - printf("MMX test\n"); + av_log(NULL, AV_LOG_INFO, "MMX test\n"); fact = 0.72; xsize = (int)(XSIZE * fact); ysize = (int)(YSIZE * fact); @@ -604,12 +816,12 @@ int main(int argc, char **argv) component_resample(s, img2, xsize, xsize, ysize, img, XSIZE, XSIZE, YSIZE); if (memcmp(img1, img2, xsize * ysize) != 0) { - fprintf(stderr, "mmx error\n"); + av_log(NULL, AV_LOG_ERROR, "mmx error\n"); exit(1); } - printf("MMX OK\n"); -#endif + av_log(NULL, AV_LOG_INFO, "MMX OK\n"); +#endif /* HAVE_MMX */ return 0; } -#endif +#endif /* TEST */