diff --git a/doc/scaler.texi b/doc/scaler.texi index 23d6393883..457347c19b 100644 --- a/doc/scaler.texi +++ b/doc/scaler.texi @@ -122,6 +122,19 @@ a_dither). @end table +@item alphablend +Set the alpha blending to use when the input has alpha but the output does not. +Default value is @samp{none}. + +@table @samp +@item uniform_color +Blend onto a uniform background color + +@item none +No blending + +@end table + @end table @c man end SCALER OPTIONS diff --git a/libswscale/Makefile b/libswscale/Makefile index a60b05748d..b11e789237 100644 --- a/libswscale/Makefile +++ b/libswscale/Makefile @@ -5,7 +5,8 @@ NAME = swscale HEADERS = swscale.h \ version.h \ -OBJS = hscale_fast_bilinear.o \ +OBJS = alphablend.o \ + hscale_fast_bilinear.o \ input.o \ options.o \ output.o \ diff --git a/libswscale/alphablend.c b/libswscale/alphablend.c new file mode 100644 index 0000000000..5833653b83 --- /dev/null +++ b/libswscale/alphablend.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Michael Niedermayer + * + * 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 + */ + +#include "swscale_internal.h" + +int ff_sws_alphablendaway(SwsContext *c, const uint8_t *src[], + int srcStride[], int srcSliceY, int srcSliceH, + uint8_t *dst[], int dstStride[]) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(c->srcFormat); + int nb_components = desc->nb_components; + int plane, x, y; + int plane_count = isGray(c->srcFormat) ? 1 : 3; + int sixteen_bits = desc->comp[0].depth_minus1 >= 8; + unsigned off = 1<comp[0].depth_minus1; + unsigned shift = desc->comp[0].depth_minus1 + 1; + unsigned max = (1<flags & AV_PIX_FMT_FLAG_PLANAR) { + for (plane = 0; plane < plane_count; plane++) { + int w = plane ? c->chrSrcW : c->srcW; + int y_subsample = plane ? desc->log2_chroma_h: 0; + for (y = srcSliceY >> y_subsample; y < FF_CEIL_RSHIFT(srcSliceH, y_subsample); y++) { + if (sixteen_bits) { + const uint16_t *s = src[plane ] + srcStride[plane] * y; + const uint16_t *a = src[plane_count] + srcStride[plane_count] * y; + uint16_t *d = dst[plane ] + dstStride[plane] * y; + unsigned target = plane && !(desc->flags & AV_PIX_FMT_FLAG_RGB) ? 1<comp[0].depth_minus1 : 0; + if ((!isBE(c->dstFormat)) == !HAVE_BIGENDIAN) { + for (x = 0; x < w; x++) { + unsigned u = s[x]*a[x] + target*(max-a[x]) + off; + d[x] = av_clip((u + (u >> shift)) >> shift, 0, max); + } + } else { + for (x = 0; x < w; x++) { + unsigned aswap =av_bswap16(a[x]); + unsigned u = av_bswap16(s[x])*aswap + target*(max-aswap) + off; + d[x] = av_clip((u + (u >> shift)) >> shift, 0, max); + } + } + } else { + const uint8_t *s = src[plane ] + srcStride[plane] * y; + const uint8_t *a = src[plane_count] + srcStride[plane_count] * y; + uint8_t *d = dst[plane ] + dstStride[plane] * y; + unsigned target = plane && !(desc->flags & AV_PIX_FMT_FLAG_RGB) ? 128 : 0; + for (x = 0; x < w; x++) { + unsigned u = s[x]*a[x] + target*(255-a[x]) + 128; + d[x] = (257*u) >> 16; + } + } + } + } + } + + return 0; +} diff --git a/libswscale/options.c b/libswscale/options.c index f08267c609..6ad6cbda2d 100644 --- a/libswscale/options.c +++ b/libswscale/options.c @@ -78,6 +78,9 @@ static const AVOption swscale_options[] = { { "gamma", "gamma correct scaling", OFFSET(gamma_flag), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE, "gamma" }, { "true", "enable", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "gamma" }, { "false", "disable", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "gamma" }, + { "alphablend", "mode for alpha -> non alpha", OFFSET(alphablend),AV_OPT_TYPE_INT, { .i64 = SWS_ALPHA_BLEND_NONE}, 0, SWS_ALPHA_BLEND_NB-1, VE, "alphablend" }, + { "none", "ignore alpha", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_ALPHA_BLEND_NONE}, INT_MIN, INT_MAX, VE, "alphablend" }, + { "uniform_color", "blend onto a uniform color", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_ALPHA_BLEND_UNIFORM},INT_MIN, INT_MAX, VE, "alphablend" }, { NULL } }; diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h index eeeef2d848..01c5ad96ba 100644 --- a/libswscale/swscale_internal.h +++ b/libswscale/swscale_internal.h @@ -75,6 +75,12 @@ typedef enum SwsDither { NB_SWS_DITHER, } SwsDither; +typedef enum SwsAlphaBlend { + SWS_ALPHA_BLEND_NONE = 0, + SWS_ALPHA_BLEND_UNIFORM, + SWS_ALPHA_BLEND_NB, +} SwsAlphaBlend; + typedef int (*SwsFunc)(struct SwsContext *context, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t *dst[], int dstStride[]); @@ -611,6 +617,8 @@ typedef struct SwsContext { int needs_hcscale; ///< Set if there are chroma planes to be converted. SwsDither dither; + + SwsAlphaBlend alphablend; } SwsContext; //FIXME check init (where 0) @@ -901,6 +909,10 @@ struct SwsContext *sws_alloc_set_opts(int srcW, int srcH, enum AVPixelFormat src int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, const double *param); +int ff_sws_alphablendaway(SwsContext *c, const uint8_t *src[], + int srcStride[], int srcSliceY, int srcSliceH, + uint8_t *dst[], int dstStride[]); + static inline void fillPlane16(uint8_t *plane, int stride, int width, int height, int y, int alpha, int bits, const int big_endian) { diff --git a/libswscale/utils.c b/libswscale/utils.c index 106101ab0b..d00164361b 100644 --- a/libswscale/utils.c +++ b/libswscale/utils.c @@ -979,6 +979,58 @@ static uint16_t * alloc_gamma_tbl(double e) return tbl; } +static enum AVPixelFormat alphaless_fmt(enum AVPixelFormat fmt) +{ + switch(fmt) { +// case AV_PIX_FMT_ARGB: return AV_PIX_FMT_RGB24; +// case AV_PIX_FMT_RGBA: return AV_PIX_FMT_RGB24; +// case AV_PIX_FMT_ABGR: return AV_PIX_FMT_BGR24; +// case AV_PIX_FMT_BGRA: return AV_PIX_FMT_BGR24; +// case AV_PIX_FMT_YA8: return AV_PIX_FMT_GRAY8; +// +// case AV_PIX_FMT_YUVA420P: return AV_PIX_FMT_YUV420P; +// case AV_PIX_FMT_YUVA422P: return AV_PIX_FMT_YUV422P; + case AV_PIX_FMT_YUVA444P: return AV_PIX_FMT_YUV444P; + + case AV_PIX_FMT_GBRAP: return AV_PIX_FMT_GBRP; + + case AV_PIX_FMT_GBRAP16LE: return AV_PIX_FMT_GBRP16; + case AV_PIX_FMT_GBRAP16BE: return AV_PIX_FMT_GBRP16; + +// case AV_PIX_FMT_RGBA64LE: return AV_PIX_FMT_RGB48; +// case AV_PIX_FMT_RGBA64BE: return AV_PIX_FMT_RGB48; +// case AV_PIX_FMT_BGRA64LE: return AV_PIX_FMT_BGR48; +// case AV_PIX_FMT_BGRA64BE: return AV_PIX_FMT_BGR48; + +// case AV_PIX_FMT_YA16BE: return AV_PIX_FMT_GRAY16; +// case AV_PIX_FMT_YA16LE: return AV_PIX_FMT_GRAY16; + +// case AV_PIX_FMT_YUVA420P9BE: return AV_PIX_FMT_YUV420P9; +// case AV_PIX_FMT_YUVA422P9BE: return AV_PIX_FMT_YUV422P9; + case AV_PIX_FMT_YUVA444P9BE: return AV_PIX_FMT_YUV444P9; +// case AV_PIX_FMT_YUVA420P9LE: return AV_PIX_FMT_YUV420P9; +// case AV_PIX_FMT_YUVA422P9LE: return AV_PIX_FMT_YUV422P9; + case AV_PIX_FMT_YUVA444P9LE: return AV_PIX_FMT_YUV444P9; +// case AV_PIX_FMT_YUVA420P10BE: return AV_PIX_FMT_YUV420P10; +// case AV_PIX_FMT_YUVA422P10BE: return AV_PIX_FMT_YUV422P10; + case AV_PIX_FMT_YUVA444P10BE: return AV_PIX_FMT_YUV444P10; +// case AV_PIX_FMT_YUVA420P10LE: return AV_PIX_FMT_YUV420P10; +// case AV_PIX_FMT_YUVA422P10LE: return AV_PIX_FMT_YUV422P10; + case AV_PIX_FMT_YUVA444P10LE: return AV_PIX_FMT_YUV444P10; +// case AV_PIX_FMT_YUVA420P16BE: return AV_PIX_FMT_YUV420P16; +// case AV_PIX_FMT_YUVA422P16BE: return AV_PIX_FMT_YUV422P16; + case AV_PIX_FMT_YUVA444P16BE: return AV_PIX_FMT_YUV444P16; +// case AV_PIX_FMT_YUVA420P16LE: return AV_PIX_FMT_YUV420P16; +// case AV_PIX_FMT_YUVA422P16LE: return AV_PIX_FMT_YUV422P16; + case AV_PIX_FMT_YUVA444P16LE: return AV_PIX_FMT_YUV444P16; + +// case AV_PIX_FMT_AYUV64LE: +// case AV_PIX_FMT_AYUV64BE: +// case AV_PIX_FMT_PAL8: + default: return AV_PIX_FMT_NONE; + } +} + av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter, SwsFilter *dstFilter) { @@ -1340,6 +1392,39 @@ av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter, } } + if (CONFIG_SWSCALE_ALPHA && isALPHA(srcFormat) && !isALPHA(dstFormat)) { + enum AVPixelFormat tmpFormat = alphaless_fmt(srcFormat); + + if (tmpFormat != AV_PIX_FMT_NONE && c->alphablend != SWS_ALPHA_BLEND_NONE) + if (!unscaled || + dstFormat != tmpFormat || + usesHFilter || usesVFilter || + c->srcRange != c->dstRange + ) { + ret = av_image_alloc(c->cascaded_tmp, c->cascaded_tmpStride, + srcW, srcH, tmpFormat, 64); + if (ret < 0) + return ret; + + c->cascaded_context[0] = sws_alloc_set_opts(srcW, srcH, srcFormat, + srcW, srcH, tmpFormat, + flags, c->param); + if (!c->cascaded_context[0]) + return -1; + c->cascaded_context[0]->alphablend = c->alphablend; + ret = sws_init_context(c->cascaded_context[0], NULL , NULL); + if (ret < 0) + return ret; + + c->cascaded_context[1] = sws_getContext(srcW, srcH, tmpFormat, + dstW, dstH, dstFormat, + flags, srcFilter, dstFilter, c->param); + if (!c->cascaded_context[1]) + return -1; + return 0; + } + } + #define USE_MMAP (HAVE_MMAP && HAVE_MPROTECT && defined MAP_ANONYMOUS) /* precalculate horizontal scaler filter coefficients */ @@ -1586,6 +1671,22 @@ av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter, c->chrXInc, c->chrYInc); } + /* alpha blend special case, note this has been split via cascaded contexts if its scaled */ + if (unscaled && !usesHFilter && !usesVFilter && + c->alphablend != SWS_ALPHA_BLEND_NONE && + isALPHA(srcFormat) && + (c->srcRange == c->dstRange || isAnyRGB(dstFormat)) && + alphaless_fmt(srcFormat) == dstFormat + ) { + c->swscale = ff_sws_alphablendaway; + + if (flags & SWS_PRINT_INFO) + av_log(c, AV_LOG_INFO, + "using alpha blendaway %s -> %s special converter\n", + av_get_pix_fmt_name(srcFormat), av_get_pix_fmt_name(dstFormat)); + return 0; + } + /* unscaled special cases */ if (unscaled && !usesHFilter && !usesVFilter && (c->srcRange == c->dstRange || isAnyRGB(dstFormat))) {