X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fimgresample.c;h=d1d6757c6c23747f495807645b6958865d8fd282;hb=f8a80fd69df1735730f1b3c7f37506ffe0570d0d;hp=d423f388cce79b6586d579badab9b800d716c883;hpb=d10dc61682b057d6f3a59aa23353e4f155f16d11;p=ffmpeg diff --git a/libavcodec/imgresample.c b/libavcodec/imgresample.c index d423f388cce..d1d6757c6c2 100644 --- a/libavcodec/imgresample.c +++ b/libavcodec/imgresample.c @@ -1,32 +1,35 @@ /* - * 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 @@ -44,14 +47,20 @@ #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_t h_filters[NB_PHASES][NB_TAPS] __align8; /* horizontal filters */ - int16_t v_filters[NB_PHASES][NB_TAPS] __align8; /* vertical filters */ + 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; }; @@ -64,8 +73,8 @@ static inline int get_phase(int pos) /* This function must be optimized */ 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_width, int src_start, int src_incr, + int16_t *filters) { int src_pos, phase, sum, i; const uint8_t *s; @@ -108,7 +117,7 @@ static void h_resample_fast(uint8_t *dst, int dst_width, const uint8_t *src, /* This function must be optimized */ static void v_resample(uint8_t *dst, int dst_width, const uint8_t *src, - int wrap, int16_t *filter) + int wrap, int16_t *filter) { int sum, i; const uint8_t *s; @@ -163,18 +172,18 @@ static void v_resample(uint8_t *dst, int dst_width, const uint8_t *src, src_pos += src_incr;\ } -#define DUMP(reg) movq_r2m(reg, tmp); printf(#reg "=%016Lx\n", tmp.uq); +#define DUMP(reg) movq_r2m(reg, tmp); printf(#reg "=%016"PRIx64"\n", tmp.uq); /* XXX: do four pixels at a time */ static void h_resample_fast4_mmx(uint8_t *dst, int dst_width, - const uint8_t *src, int src_width, + const uint8_t *src, int src_width, int src_start, int src_incr, int16_t *filters) { int src_pos, phase; const uint8_t *s; int16_t *filter; mmx_t tmp; - + src_pos = src_start; pxor_r2r(mm7, mm7); @@ -212,13 +221,13 @@ static void h_resample_fast4_mmx(uint8_t *dst, int dst_width, } static void v_resample4_mmx(uint8_t *dst, int dst_width, const uint8_t *src, - int wrap, int16_t *filter) + int wrap, int16_t *filter) { int sum, i, v; const uint8_t *s; mmx_t tmp; mmx_t coefs[4]; - + for(i=0;i<4;i++) { v = filter[i]; coefs[i].uw[0] = v; @@ -226,7 +235,7 @@ static void v_resample4_mmx(uint8_t *dst, int dst_width, const uint8_t *src, coefs[i].uw[2] = v; coefs[i].uw[3] = v; } - + pxor_r2r(mm7, mm7); s = src; while (dst_width >= 4) { @@ -248,7 +257,7 @@ static void v_resample4_mmx(uint8_t *dst, int dst_width, const uint8_t *src, paddw_r2r(mm3, mm2); paddw_r2r(mm2, mm0); psraw_i2r(FILTER_BITS, mm0); - + packuswb_r2r(mm7, mm0); movq_r2m(mm0, tmp); @@ -274,138 +283,11 @@ static void v_resample4_mmx(uint8_t *dst, int dst_width, const uint8_t *src, } emms(); } -#endif - -#ifdef HAVE_ALTIVEC -typedef union { - vector unsigned char v; - unsigned char c[16]; -} vec_uc_t; - -typedef union { - vector signed short v; - signed short s[8]; -} vec_ss_t; - -void v_resample16_altivec(uint8_t *dst, int dst_width, const uint8_t *src, - int wrap, int16_t *filter) -{ - int sum, i; - const uint8_t *s; - vector unsigned char *tv, tmp, dstv, zero; - vec_ss_t srchv[4], srclv[4], fv[4]; - vector signed short zeros, sumhv, sumlv; - s = src; - - for(i=0;i<4;i++) - { - /* - The vec_madds later on does an implicit >>15 on the result. - Since FILTER_BITS is 8, and we have 15 bits of magnitude in - a signed short, we have just enough bits to pre-shift our - filter constants <<7 to compensate for vec_madds. - */ - fv[i].s[0] = filter[i] << (15-FILTER_BITS); - fv[i].v = vec_splat(fv[i].v, 0); - } - - zero = vec_splat_u8(0); - zeros = vec_splat_s16(0); - - - /* - When we're resampling, we'd ideally like both our input buffers, - and output buffers to be 16-byte aligned, so we can do both aligned - reads and writes. Sadly we can't always have this at the moment, so - we opt for aligned writes, as unaligned writes have a huge overhead. - To do this, do enough scalar resamples to get dst 16-byte aligned. - */ - i = (-(int)dst) & 0xf; - while(i>0) { - sum = s[0 * wrap] * filter[0] + - s[1 * wrap] * filter[1] + - s[2 * wrap] * filter[2] + - s[3 * wrap] * filter[3]; - sum = sum >> FILTER_BITS; - if (sum<0) sum = 0; else if (sum>255) sum=255; - dst[0] = sum; - dst++; - s++; - dst_width--; - i--; - } - - /* Do our altivec resampling on 16 pixels at once. */ - while(dst_width>=16) { - /* - Read 16 (potentially unaligned) bytes from each of - 4 lines into 4 vectors, and split them into shorts. - Interleave the multipy/accumulate for the resample - filter with the loads to hide the 3 cycle latency - the vec_madds have. - */ - tv = (vector unsigned char *) &s[0 * wrap]; - tmp = vec_perm(tv[0], tv[1], vec_lvsl(0, &s[i * wrap])); - srchv[0].v = (vector signed short) vec_mergeh(zero, tmp); - srclv[0].v = (vector signed short) vec_mergel(zero, tmp); - sumhv = vec_madds(srchv[0].v, fv[0].v, zeros); - sumlv = vec_madds(srclv[0].v, fv[0].v, zeros); - - tv = (vector unsigned char *) &s[1 * wrap]; - tmp = vec_perm(tv[0], tv[1], vec_lvsl(0, &s[1 * wrap])); - srchv[1].v = (vector signed short) vec_mergeh(zero, tmp); - srclv[1].v = (vector signed short) vec_mergel(zero, tmp); - sumhv = vec_madds(srchv[1].v, fv[1].v, sumhv); - sumlv = vec_madds(srclv[1].v, fv[1].v, sumlv); - - tv = (vector unsigned char *) &s[2 * wrap]; - tmp = vec_perm(tv[0], tv[1], vec_lvsl(0, &s[2 * wrap])); - srchv[2].v = (vector signed short) vec_mergeh(zero, tmp); - srclv[2].v = (vector signed short) vec_mergel(zero, tmp); - sumhv = vec_madds(srchv[2].v, fv[2].v, sumhv); - sumlv = vec_madds(srclv[2].v, fv[2].v, sumlv); - - tv = (vector unsigned char *) &s[3 * wrap]; - tmp = vec_perm(tv[0], tv[1], vec_lvsl(0, &s[3 * wrap])); - srchv[3].v = (vector signed short) vec_mergeh(zero, tmp); - srclv[3].v = (vector signed short) vec_mergel(zero, tmp); - sumhv = vec_madds(srchv[3].v, fv[3].v, sumhv); - sumlv = vec_madds(srclv[3].v, fv[3].v, sumlv); - - /* - Pack the results into our destination vector, - and do an aligned write of that back to memory. - */ - dstv = vec_packsu(sumhv, sumlv) ; - vec_st(dstv, 0, (vector unsigned char *) dst); - - dst+=16; - s+=16; - dst_width-=16; - } - - /* - If there are any leftover pixels, resample them - with the slow scalar method. - */ - while(dst_width>0) { - sum = s[0 * wrap] * filter[0] + - s[1 * wrap] * filter[1] + - s[2 * wrap] * filter[2] + - s[3 * wrap] * filter[3]; - sum = sum >> FILTER_BITS; - if (sum<0) sum = 0; else if (sum>255) sum=255; - dst[0] = sum; - dst++; - s++; - dst_width--; - } -} -#endif +#endif /* HAVE_MMX */ /* slow version to handle limit cases. Does not need optimisation */ static void h_resample_slow(uint8_t *dst, int dst_width, - const uint8_t *src, int src_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; @@ -441,8 +323,8 @@ static void h_resample_slow(uint8_t *dst, int dst_width, } 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 src_width, int src_start, int src_incr, + int16_t *filters) { int n, src_end; @@ -455,29 +337,29 @@ static void h_resample(uint8_t *dst, int dst_width, const uint8_t *src, } 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, +static void component_resample(ImgReSampleContext *s, uint8_t *output, int owrap, int owidth, int oheight, uint8_t *input, int iwrap, int iwidth, int iheight) { @@ -486,7 +368,7 @@ static void component_resample(ImgReSampleContext *s, 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;yline_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) { @@ -520,8 +402,8 @@ 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 @@ -532,12 +414,12 @@ static void component_resample(ImgReSampleContext *s, &s->v_filters[phase_y][0]); else #endif - v_resample(output, owidth, - s->line_buf + (ring_y - NB_TAPS + 1) * owidth, owidth, + 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; } } @@ -545,7 +427,7 @@ static void component_resample(ImgReSampleContext *s, ImgReSampleContext *img_resample_init(int owidth, int oheight, int iwidth, int iheight) { - return img_resample_full_init(owidth, oheight, iwidth, iheight, + return img_resample_full_init(owidth, oheight, iwidth, iheight, 0, 0, 0, 0, 0, 0, 0, 0); } @@ -559,7 +441,7 @@ ImgReSampleContext *img_resample_full_init(int owidth, int oheight, ImgReSampleContext *s; if (!owidth || !oheight || !iwidth || !iheight) - return NULL; + return NULL; s = av_mallocz(sizeof(ImgReSampleContext)); if (!s) @@ -567,19 +449,19 @@ ImgReSampleContext *img_resample_full_init(int owidth, int oheight, if((unsigned)owidth >= 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->topBand = topBand; s->bottomBand = bottomBand; s->leftBand = leftBand; s->rightBand = rightBand; - + s->padtop = padtop; s->padbottom = padbottom; s->padleft = padleft; @@ -589,11 +471,11 @@ ImgReSampleContext *img_resample_full_init(int owidth, int oheight, 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; + s->v_incr = ((iheight - topBand - bottomBand) * POS_FRAC) / s->pad_oheight; - av_build_filter(&s->h_filters[0][0], (float) s->pad_owidth / + 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 / + av_build_filter(&s->v_filters[0][0], (float) s->pad_oheight / (float) (iheight - topBand - bottomBand), NB_TAPS, NB_PHASES, 1<data[i] + (((output->linesize[i] * + optr = output->data[i] + (((output->linesize[i] * s->padtop) + s->padleft) >> shift); - component_resample(s, optr, output->linesize[i], + component_resample(s, optr, output->linesize[i], s->pad_owidth >> shift, s->pad_oheight >> shift, - input->data[i] + (input->linesize[i] * + input->data[i] + (input->linesize[i] * (s->topBand >> shift)) + (s->leftBand >> shift), - input->linesize[i], ((s->iwidth - s->leftBand - + input->linesize[i], ((s->iwidth - s->leftBand - s->rightBand) >> shift), (s->iheight - s->topBand - s->bottomBand) >> shift); } @@ -630,8 +512,187 @@ void img_resample_close(ImgReSampleContext *s) av_free(s); } +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; + } + + 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[]) +{ + 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 @@ -690,20 +751,20 @@ int main(int argc, char **argv) else v = 0x00; } else if (x < XSIZE/4) { - if (x & 1) + if (x & 1) v = 0xff; - else + else v = 0; } else if (y < XSIZE/4) { - if (y & 1) + if (y & 1) v = 0xff; - else + else v = 0; } else { if (y < YSIZE*3/8) { - if ((y+x) & 1) + if ((y+x) & 1) v = 0xff; - else + else v = 0; } else { if (((x+3) % 4) <= 1 && @@ -759,8 +820,8 @@ int main(int argc, char **argv) exit(1); } av_log(NULL, AV_LOG_INFO, "MMX OK\n"); -#endif +#endif /* HAVE_MMX */ return 0; } -#endif +#endif /* TEST */