[FFmpeg-devel] [PATCH] swscale: Implement alphablendaway for planar 4:4:4 formats
Michael Niedermayer
michaelni at gmx.at
Thu Aug 6 22:27:35 CEST 2015
From: Michael Niedermayer <michael at niedermayer.cc>
Fixes Ticket4746
TODO: implement packed formats and subsampled formats
Signed-off-by: Michael Niedermayer <michael at niedermayer.cc>
---
doc/scaler.texi | 13 ++++++
libswscale/Makefile | 3 +-
libswscale/alphablend.c | 55 +++++++++++++++++++++++
libswscale/options.c | 3 ++
libswscale/swscale_internal.h | 12 +++++
libswscale/utils.c | 97 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 182 insertions(+), 1 deletion(-)
create mode 100644 libswscale/alphablend.c
diff --git a/doc/scaler.texi b/doc/scaler.texi
index 23d6393..4c55334 100644
--- a/doc/scaler.texi
+++ b/doc/scaler.texi
@@ -122,6 +122,19 @@ a_dither).
@end table
+ at item alphablend
+Set the alpha blending to use when the input has alpha but the output does not.
+Default value is @samp{black}.
+
+ at table @samp
+ at item black
+Blend onto black
+
+ at item none
+No blending
+
+ at end table
+
@end table
@c man end SCALER OPTIONS
diff --git a/libswscale/Makefile b/libswscale/Makefile
index a60b057..b11e789 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 0000000..d970b62
--- /dev/null
+++ b/libswscale/alphablend.c
@@ -0,0 +1,55 @@
+
+#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<<desc->comp[0].depth_minus1;
+ unsigned shift = desc->comp[0].depth_minus1 + 1;
+ unsigned max = (1<<shift) - 1;
+
+ av_assert0(plane_count == nb_components - 1);
+ if (desc->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<<desc->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;
+}
\ No newline at end of file
diff --git a/libswscale/options.c b/libswscale/options.c
index f08267c..8800760 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_BLACK}, 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" },
+ { "black", "blend onto black", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_ALPHA_BLEND_BLACK},INT_MIN, INT_MAX, VE, "alphablend" },
{ NULL }
};
diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h
index 737c179..50b59a3 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_BLACK,
+ 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)
@@ -890,6 +898,10 @@ void ff_hcscale_fast_mmxext(SwsContext *c, int16_t *dst1, int16_t *dst2,
int dstWidth, const uint8_t *src1,
const uint8_t *src2, int srcW, int xInc);
+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 565c904..7fc96ae 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,35 @@ 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_getContext(srcW, srcH, srcFormat,
+ srcW, srcH, tmpFormat,
+ flags, NULL , NULL, c->param);
+ if (!c->cascaded_context[0])
+ return -1;
+
+ 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 +1667,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))) {
--
1.7.9.5
More information about the ffmpeg-devel
mailing list