From patchwork Sun Nov 18 13:10:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 11065 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 605CB44C43C for ; Sun, 18 Nov 2018 15:10:53 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id D12D168034D; Sun, 18 Nov 2018 15:10:53 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f66.google.com (mail-wr1-f66.google.com [209.85.221.66]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 52ED668034D for ; Sun, 18 Nov 2018 15:10:47 +0200 (EET) Received: by mail-wr1-f66.google.com with SMTP id u9-v6so29207448wrr.0 for ; Sun, 18 Nov 2018 05:10:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id; bh=uCbObpHJ/oCEDbFst+oF87oquIp1mWL8dCeU0Wrx4/g=; b=ksLMJb7BLrMO8W+BICYi6aIuOSKpgBkfZO+cuVxwZGlwfGGlL1GQmkXRFkPlBD5Jqr U3mDFL5zp/UIC2GxokY09nqR90ufR8WFR5rF7n4T3nswJ2SzpVu6GGvCt5SXlUHCPuyD lrFUi2kKjeaFJDyrsk2XCQIDQYUZCcbhTZrFGJW3aUrOBXmh4z+hr6AiRW+3zxCviEDa lidDAX5Vgf8a2aYxS8jZx/bivvSNrcfbx51REQq5mGDagGeiKdFX0QHlwHTQHW8eokNw KuLsZ3qLBwWMB/1m6rz6dZDFZ/J6Fu7/GtjfGApJHSntmwkwion7jnhEsmEvUzQh3WZP AvNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=uCbObpHJ/oCEDbFst+oF87oquIp1mWL8dCeU0Wrx4/g=; b=HMBlYJW8tWDz3AIw2FYa4JWX0J8/DDdJTtycAOV/iykCaZ0fLjOyDt5txSYiQSl6qG dEFC7NAGixQZ1Qro0SJRDahrXSAEzxJA+l7QYOULQrs2yeUYCsS7bKSkd8C6+BTlU/kG 0FD3tY6xQ51NepXO64wVaDuGaFaP4uukOne1rXUurzkmfv4zIc0EI6P/Dpdlr/lBHM40 Mg8bN1qgxrvYGYGrFhkmlStXDp7byfZXNKMO/akBeYVQ7qsKuJEby0Ha002LH4jeO9KS gzCjYPYayUXhQAE1+ruxVnMOXtaXbvwDxb9eEO4NeYA/ofAwEPW332F/DSiw5lLs9uCy 6vsg== X-Gm-Message-State: AGRZ1gKAbf3VmJmoea8H+IM59XrXkYwR9xDqQdh0+kfVkJTQSg4yVtTT 41pHI5NFKoFYCaykjw+crhM23R3j X-Google-Smtp-Source: AJdET5f2I+WTfz91GCgnPeJ976uoF0qWVGcecvGmta0/LgeKssjM/rMkON4F7qjQP3TxKFxVV2Xd9Q== X-Received: by 2002:adf:90af:: with SMTP id i44-v6mr14130284wri.77.1542546647580; Sun, 18 Nov 2018 05:10:47 -0800 (PST) Received: from localhost.localdomain ([94.250.174.60]) by smtp.gmail.com with ESMTPSA id t5sm11977943wmg.43.2018.11.18.05.10.46 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 18 Nov 2018 05:10:47 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Sun, 18 Nov 2018 14:10:37 +0100 Message-Id: <20181118131037.32440-1-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH] avfilter: add rgbashift filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- doc/filters.texi | 25 ++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_chromashift.c | 226 +++++++++++++++++++++++++++++++++-- 4 files changed, 244 insertions(+), 9 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 77f57bfcd6..6b1bf8766b 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -14311,6 +14311,31 @@ trim=end=5,reverse @end example @end itemize +@section rgbashift +Shift R/G/B/A pixels horizontally and/or vertically. + +The filter accepts the following options: +@table @option +@item rh +Set amount to shift red horizontally. +@item rv +Set amount to shift red vertically. +@item gh +Set amount to shift green horizontally. +@item gv +Set amount to shift green vertically. +@item bh +Set amount to shift blue horizontally. +@item bv +Set amount to shift blue vertically. +@item ah +Set amount to shift alpha horizontally. +@item av +Set amount to shift alpha vertically. +@item edge +Set edge mode, can be @var{smear}, default, or @var{warp}. +@end table + @section roberts Apply roberts cross operator to input video stream. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 8f3fde8c27..4df22db2fc 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -330,6 +330,7 @@ OBJS-$(CONFIG_REMOVEGRAIN_FILTER) += vf_removegrain.o OBJS-$(CONFIG_REMOVELOGO_FILTER) += bbox.o lswsutils.o lavfutils.o vf_removelogo.o OBJS-$(CONFIG_REPEATFIELDS_FILTER) += vf_repeatfields.o OBJS-$(CONFIG_REVERSE_FILTER) += f_reverse.o +OBJS-$(CONFIG_RGBASHIFT_FILTER) += vf_chromashift.o OBJS-$(CONFIG_ROBERTS_FILTER) += vf_convolution.o OBJS-$(CONFIG_ROBERTS_OPENCL_FILTER) += vf_convolution_opencl.o opencl.o \ opencl/convolution.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 6f392b5632..7294486314 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -314,6 +314,7 @@ extern AVFilter ff_vf_removegrain; extern AVFilter ff_vf_removelogo; extern AVFilter ff_vf_repeatfields; extern AVFilter ff_vf_reverse; +extern AVFilter ff_vf_rgbashift; extern AVFilter ff_vf_roberts; extern AVFilter ff_vf_roberts_opencl; extern AVFilter ff_vf_rotate; diff --git a/libavfilter/vf_chromashift.c b/libavfilter/vf_chromashift.c index f4c561e2d0..3e0557036a 100644 --- a/libavfilter/vf_chromashift.c +++ b/libavfilter/vf_chromashift.c @@ -35,8 +35,13 @@ typedef struct ChromaShiftContext { const AVClass *class; int cbh, cbv; int crh, crv; + int rh, rv; + int gh, gv; + int bh, bv; + int ah, av; int edge; + int nb_planes; int depth; int height[4]; int width[4]; @@ -44,12 +49,13 @@ typedef struct ChromaShiftContext { AVFrame *in; + int is_rgbashift; int (*filter_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); } ChromaShiftContext; static int query_formats(AVFilterContext *ctx) { - static const enum AVPixelFormat pix_fmts[] = { + static const enum AVPixelFormat yuv_pix_fmts[] = { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P,AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, @@ -62,6 +68,19 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_NONE }; + static const enum AVPixelFormat rgb_pix_fmts[] = { + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP9, + AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, + AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, + AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_NONE + }; + const enum AVPixelFormat *pix_fmts; + + if (!strcmp(ctx->filter->name, "rgbashift")) + pix_fmts = rgb_pix_fmts; + else + pix_fmts = yuv_pix_fmts; AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); if (!fmts_list) @@ -163,6 +182,156 @@ static int wrap_slice ## depth(AVFilterContext *ctx, void *arg, int jobnr, int n DEFINE_WRAP(8, uint8_t, 1) DEFINE_WRAP(16, uint16_t, 2) +#define DEFINE_RGBASMEAR(depth, type, div) \ +static int rgbasmear_slice ## depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ +{ \ + ChromaShiftContext *s = ctx->priv; \ + AVFrame *in = s->in; \ + AVFrame *out = arg; \ + const int srlinesize = in->linesize[2] / div; \ + const int sglinesize = in->linesize[0] / div; \ + const int sblinesize = in->linesize[1] / div; \ + const int salinesize = in->linesize[3] / div; \ + const int rlinesize = out->linesize[2] / div; \ + const int glinesize = out->linesize[0] / div; \ + const int blinesize = out->linesize[1] / div; \ + const int alinesize = out->linesize[3] / div; \ + const int rh = s->rh; \ + const int rv = s->rv; \ + const int gh = s->gh; \ + const int gv = s->gv; \ + const int bh = s->bh; \ + const int bv = s->bv; \ + const int ah = s->ah; \ + const int av = s->av; \ + const int h = s->height[1]; \ + const int w = s->width[1]; \ + const int slice_start = (h * jobnr) / nb_jobs; \ + const int slice_end = (h * (jobnr+1)) / nb_jobs; \ + const type *sr = (const type *)in->data[2]; \ + const type *sg = (const type *)in->data[0]; \ + const type *sb = (const type *)in->data[1]; \ + const type *sa = (const type *)in->data[3]; \ + type *dr = (type *)out->data[2] + slice_start * rlinesize; \ + type *dg = (type *)out->data[0] + slice_start * glinesize; \ + type *db = (type *)out->data[1] + slice_start * blinesize; \ + type *da = (type *)out->data[3] + slice_start * alinesize; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < w; x++) { \ + dr[x] = sr[av_clip(x - rh, 0, w - 1) + av_clip(y - rv, 0, h-1) * srlinesize]; \ + dg[x] = sg[av_clip(x - gh, 0, w - 1) + av_clip(y - gv, 0, h-1) * sglinesize]; \ + db[x] = sb[av_clip(x - bh, 0, w - 1) + av_clip(y - bv, 0, h-1) * sblinesize]; \ + } \ + \ + dr += rlinesize; \ + dg += glinesize; \ + db += blinesize; \ + \ + if (s->nb_planes < 4) \ + continue; \ + for (int x = 0; x < w; x++) { \ + da[x] = sa[av_clip(x - ah, 0, w - 1) + av_clip(y - av, 0, h-1) * salinesize]; \ + } \ + \ + da += alinesize; \ + } \ + \ + return 0; \ +} + +DEFINE_RGBASMEAR(8, uint8_t, 1) +DEFINE_RGBASMEAR(16, uint16_t, 2) + +#define DEFINE_RGBAWRAP(depth, type, div) \ +static int rgbawrap_slice ## depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ +{ \ + ChromaShiftContext *s = ctx->priv; \ + AVFrame *in = s->in; \ + AVFrame *out = arg; \ + const int srlinesize = in->linesize[2] / div; \ + const int sglinesize = in->linesize[0] / div; \ + const int sblinesize = in->linesize[1] / div; \ + const int salinesize = in->linesize[3] / div; \ + const int rlinesize = out->linesize[2] / div; \ + const int glinesize = out->linesize[0] / div; \ + const int blinesize = out->linesize[1] / div; \ + const int alinesize = out->linesize[3] / div; \ + const int rh = s->rh; \ + const int rv = s->rv; \ + const int gh = s->gh; \ + const int gv = s->gv; \ + const int bh = s->bh; \ + const int bv = s->bv; \ + const int ah = s->ah; \ + const int av = s->av; \ + const int h = s->height[1]; \ + const int w = s->width[1]; \ + const int slice_start = (h * jobnr) / nb_jobs; \ + const int slice_end = (h * (jobnr+1)) / nb_jobs; \ + const type *sr = (const type *)in->data[2]; \ + const type *sg = (const type *)in->data[0]; \ + const type *sb = (const type *)in->data[1]; \ + const type *sa = (const type *)in->data[3]; \ + type *dr = (type *)out->data[2] + slice_start * rlinesize; \ + type *dg = (type *)out->data[0] + slice_start * glinesize; \ + type *db = (type *)out->data[1] + slice_start * blinesize; \ + type *da = (type *)out->data[3] + slice_start * alinesize; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + int ry = (y - rv) % h; \ + int gy = (y - gv) % h; \ + int by = (y - bv) % h; \ + \ + if (ry < 0) \ + ry += h; \ + if (gy < 0) \ + gy += h; \ + if (by < 0) \ + by += h; \ + \ + for (int x = 0; x < w; x++) { \ + int rx = (x - rh) % w; \ + int gx = (x - gh) % w; \ + int bx = (x - bh) % w; \ + \ + if (rx < 0) \ + rx += w; \ + if (gx < 0) \ + gx += w; \ + if (bx < 0) \ + bx += w; \ + dr[x] = sr[rx + ry * srlinesize]; \ + dg[x] = sg[gx + gy * sglinesize]; \ + db[x] = sb[bx + by * sblinesize]; \ + } \ + \ + dr += rlinesize; \ + dg += glinesize; \ + db += blinesize; \ + \ + if (s->nb_planes < 4) \ + continue; \ + for (int x = 0; x < w; x++) { \ + int ax = (x - ah) % w; \ + int ay = (x - av) % h; \ + \ + if (ax < 0) \ + ax += w; \ + if (ay < 0) \ + ay += h; \ + da[x] = sa[ax + ay * salinesize]; \ + } \ + \ + da += alinesize; \ + } \ + \ + return 0; \ +} + +DEFINE_RGBAWRAP(8, uint8_t, 1) +DEFINE_RGBAWRAP(16, uint16_t, 2) + static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; @@ -178,10 +347,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_copy_props(out, in); s->in = in; - av_image_copy_plane(out->data[0] + out->linesize[0], - out->linesize[0], - in->data[0], in->linesize[0], - s->linesize[0], s->height[0]); + if (!s->is_rgbashift) { + av_image_copy_plane(out->data[0] + out->linesize[0], + out->linesize[0], + in->data[0], in->linesize[0], + s->linesize[0], s->height[0]); + } ctx->internal->execute(ctx, s->filter_slice, out, NULL, FFMIN3(s->height[1], s->height[2], @@ -197,11 +368,20 @@ static int config_input(AVFilterLink *inlink) ChromaShiftContext *s = ctx->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + s->is_rgbashift = !strcmp(ctx->filter->name, "rgbashift"); s->depth = desc->comp[0].depth; - if (s->edge) - s->filter_slice = s->depth > 8 ? wrap_slice16 : wrap_slice8; - else - s->filter_slice = s->depth > 8 ? smear_slice16 : smear_slice8; + s->nb_planes = desc->nb_components; + if (s->is_rgbashift) { + if (s->edge) + s->filter_slice = s->depth > 8 ? rgbawrap_slice16 : rgbawrap_slice8; + else + s->filter_slice = s->depth > 8 ? rgbasmear_slice16 : rgbasmear_slice8; + } else { + if (s->edge) + s->filter_slice = s->depth > 8 ? wrap_slice16 : wrap_slice8; + else + s->filter_slice = s->depth > 8 ? smear_slice16 : smear_slice8; + } s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); s->height[0] = s->height[3] = inlink->h; s->width[1] = s->width[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); @@ -254,3 +434,31 @@ AVFilter ff_vf_chromashift = { .inputs = inputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, }; + +static const AVOption rgbashift_options[] = { + { "rh", "shift red horizontally", OFFSET(rh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, + { "rv", "shift red vertically", OFFSET(rv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, + { "gh", "shift green horizontally", OFFSET(gh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, + { "gv", "shift green vertically", OFFSET(gv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, + { "bh", "shift blue horizontally", OFFSET(bh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, + { "bv", "shift blue vertically", OFFSET(bv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, + { "ah", "shift alpha horizontally", OFFSET(ah), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, + { "av", "shift alpha vertically", OFFSET(av), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, + { "edge", "set edge operation", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = VF, "edge" }, + { "smear", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = VF, "edge" }, + { "wrap", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = VF, "edge" }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(rgbashift); + +AVFilter ff_vf_rgbashift = { + .name = "rgbashift", + .description = NULL_IF_CONFIG_SMALL("Shift RGBA."), + .priv_size = sizeof(ChromaShiftContext), + .priv_class = &rgbashift_class, + .query_formats = query_formats, + .outputs = outputs, + .inputs = inputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, +};