swscale: Implement alphablendaway for planar 4:4:4 formats
authorMichael Niedermayer <michael@niedermayer.cc>
Thu, 6 Aug 2015 14:36:05 +0000 (16:36 +0200)
committerMichael Niedermayer <michael@niedermayer.cc>
Sat, 8 Aug 2015 11:24:52 +0000 (13:24 +0200)
Fixes Ticket4746

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
doc/scaler.texi
libswscale/Makefile
libswscale/alphablend.c [new file with mode: 0644]
libswscale/options.c
libswscale/swscale_internal.h
libswscale/utils.c

index 23d6393..457347c 100644 (file)
@@ -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
index a60b057..b11e789 100644 (file)
@@ -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 (file)
index 0000000..5833653
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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<<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;
+}
index f08267c..6ad6cbd 100644 (file)
@@ -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 }
 };
index eeeef2d..01c5ad9 100644 (file)
@@ -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)
 {
index 106101a..d001643 100644 (file)
@@ -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))) {