Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright (c) 2012-2013 Oka Motofumi (chikuzen.mo at gmail dot com)
  3.  * Copyright (c) 2015 Paul B Mahol
  4.  *
  5.  * This file is part of FFmpeg.
  6.  *
  7.  * FFmpeg is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * FFmpeg is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with FFmpeg; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20.  */
  21.  
  22. #include "libavutil/imgutils.h"
  23. #include "libavutil/pixdesc.h"
  24. #include "libavutil/opt.h"
  25. #include "avfilter.h"
  26. #include "formats.h"
  27. #include "internal.h"
  28. #include "video.h"
  29.  
  30. typedef struct NContext {
  31.     const AVClass *class;
  32.     int planeheight[4];
  33.     int planewidth[4];
  34.     int nb_planes;
  35.     int threshold[4];
  36.     int coordinates;
  37.     uint8_t *buffer;
  38.  
  39.     void (*filter)(uint8_t *dst, const uint8_t *p1, int width,
  40.                    int threshold, const uint8_t *coordinates[], int coord);
  41. } NContext;
  42.  
  43. static int query_formats(AVFilterContext *ctx)
  44. {
  45.     static const enum AVPixelFormat pix_fmts[] = {
  46.         AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P,
  47.         AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P,AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
  48.         AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
  49.         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE
  50.     };
  51.  
  52.     ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  53.     return 0;
  54. }
  55.  
  56. static av_cold void uninit(AVFilterContext *ctx)
  57. {
  58.     NContext *s = ctx->priv;
  59.  
  60.     av_freep(&s->buffer);
  61. }
  62.  
  63. static inline void line_copy8(uint8_t *line, const uint8_t *srcp, int width, int mergin)
  64. {
  65.     int i;
  66.  
  67.     memcpy(line, srcp, width);
  68.  
  69.     for (i = mergin; i > 0; i--) {
  70.         line[-i] = line[i];
  71.         line[width - 1 + i] = line[width - 1 - i];
  72.     }
  73. }
  74.  
  75. static void erosion(uint8_t *dst, const uint8_t *p1, int width,
  76.                     int threshold, const uint8_t *coordinates[], int coord)
  77. {
  78.     int x, i;
  79.  
  80.     for (x = 0; x < width; x++) {
  81.         int min = p1[x];
  82.         int limit = FFMAX(min - threshold, 0);
  83.  
  84.         for (i = 0; i < 8; i++) {
  85.             if (coord & (1 << i)) {
  86.                 min = FFMIN(min, *(coordinates[i] + x));
  87.             }
  88.             min = FFMAX(min, limit);
  89.         }
  90.  
  91.         dst[x] = min;
  92.     }
  93. }
  94.  
  95. static void dilation(uint8_t *dst, const uint8_t *p1, int width,
  96.                      int threshold, const uint8_t *coordinates[], int coord)
  97. {
  98.     int x, i;
  99.  
  100.     for (x = 0; x < width; x++) {
  101.         int max = p1[x];
  102.         int limit = FFMIN(max + threshold, 255);
  103.  
  104.         for (i = 0; i < 8; i++) {
  105.             if (coord & (1 << i)) {
  106.                 max = FFMAX(max, *(coordinates[i] + x));
  107.             }
  108.             max = FFMIN(max, limit);
  109.         }
  110.  
  111.         dst[x] = max;
  112.     }
  113. }
  114.  
  115. static void deflate(uint8_t *dst, const uint8_t *p1, int width,
  116.                     int threshold, const uint8_t *coordinates[], int coord)
  117. {
  118.     int x, i;
  119.  
  120.     for (x = 0; x < width; x++) {
  121.         int sum = 0;
  122.         int limit = FFMAX(p1[x] - threshold, 0);
  123.  
  124.         for (i = 0; i < 8; sum += *(coordinates[i++] + x));
  125.  
  126.         dst[x] = FFMAX(FFMIN(sum / 8, p1[x]), limit);
  127.     }
  128. }
  129.  
  130. static void inflate(uint8_t *dst, const uint8_t *p1, int width,
  131.                     int threshold, const uint8_t *coordinates[], int coord)
  132. {
  133.     int x, i;
  134.  
  135.     for (x = 0; x < width; x++) {
  136.         int sum = 0;
  137.         int limit = FFMIN(p1[x] + threshold, 255);
  138.  
  139.         for (i = 0; i < 8; sum += *(coordinates[i++] + x));
  140.  
  141.         dst[x] = FFMIN(FFMAX(sum / 8, p1[x]), limit);
  142.     }
  143. }
  144.  
  145. static int config_input(AVFilterLink *inlink)
  146. {
  147.     AVFilterContext *ctx = inlink->dst;
  148.     NContext *s = ctx->priv;
  149.     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  150.     int ret;
  151.  
  152.     if ((ret = av_image_fill_linesizes(s->planewidth, inlink->format, inlink->w)) < 0)
  153.         return ret;
  154.  
  155.     s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
  156.     s->planeheight[0] = s->planeheight[3] = inlink->h;
  157.  
  158.     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
  159.     s->buffer = av_malloc(3 * (s->planewidth[0] + 32));
  160.     if (!s->buffer)
  161.         return AVERROR(ENOMEM);
  162.  
  163.     if (!strcmp(ctx->filter->name, "erosion"))
  164.         s->filter = erosion;
  165.     else if (!strcmp(ctx->filter->name, "dilation"))
  166.         s->filter = dilation;
  167.     else if (!strcmp(ctx->filter->name, "deflate"))
  168.         s->filter = deflate;
  169.     else if (!strcmp(ctx->filter->name, "inflate"))
  170.         s->filter = inflate;
  171.  
  172.     return 0;
  173. }
  174.  
  175. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  176. {
  177.     AVFilterContext *ctx = inlink->dst;
  178.     AVFilterLink *outlink = ctx->outputs[0];
  179.     NContext *s = ctx->priv;
  180.     AVFrame *out;
  181.     int plane, y;
  182.  
  183.     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  184.     if (!out) {
  185.         av_frame_free(&in);
  186.         return AVERROR(ENOMEM);
  187.     }
  188.     av_frame_copy_props(out, in);
  189.  
  190.     for (plane = 0; plane < s->nb_planes; plane++) {
  191.         const int threshold = s->threshold[plane];
  192.  
  193.         if (threshold) {
  194.             const uint8_t *src = in->data[plane];
  195.             uint8_t *dst = out->data[plane];
  196.             int stride = in->linesize[plane];
  197.             int height = s->planeheight[plane];
  198.             int width  = s->planewidth[plane];
  199.             uint8_t *p0 = s->buffer + 16;
  200.             uint8_t *p1 = p0 + s->planewidth[0];
  201.             uint8_t *p2 = p1 + s->planewidth[0];
  202.             uint8_t *orig = p0, *end = p2;
  203.  
  204.             line_copy8(p0, src + stride, width, 1);
  205.             line_copy8(p1, src, width, 1);
  206.  
  207.             for (y = 0; y < height; y++) {
  208.                 const uint8_t *coordinates[] = { p0 - 1, p0, p0 + 1,
  209.                                                  p1 - 1,     p1 + 1,
  210.                                                  p2 - 1, p2, p2 + 1};
  211.                 src += stride * (y < height - 1 ? 1 : -1);
  212.                 line_copy8(p2, src, width, 1);
  213.  
  214.                 s->filter(dst, p1, width, threshold, coordinates, s->coordinates);
  215.  
  216.                 p0 = p1;
  217.                 p1 = p2;
  218.                 p2 = (p2 == end) ? orig: p2 + s->planewidth[0];
  219.                 dst += out->linesize[plane];
  220.             }
  221.         } else {
  222.             av_image_copy_plane(out->data[plane], out->linesize[plane],
  223.                                 in->data[plane], in->linesize[plane],
  224.                                 s->planewidth[plane], s->planeheight[plane]);
  225.         }
  226.     }
  227.  
  228.     av_frame_free(&in);
  229.     return ff_filter_frame(outlink, out);
  230. }
  231.  
  232. static const AVFilterPad neighbor_inputs[] = {
  233.     {
  234.         .name         = "default",
  235.         .type         = AVMEDIA_TYPE_VIDEO,
  236.         .filter_frame = filter_frame,
  237.         .config_props = config_input,
  238.     },
  239.     { NULL }
  240. };
  241.  
  242. static const AVFilterPad neighbor_outputs[] = {
  243.     {
  244.         .name = "default",
  245.         .type = AVMEDIA_TYPE_VIDEO,
  246.     },
  247.     { NULL }
  248. };
  249.  
  250. #define OFFSET(x) offsetof(NContext, x)
  251. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  252.  
  253. #define DEFINE_NEIGHBOR_FILTER(name_, description_)          \
  254. AVFILTER_DEFINE_CLASS(name_);                                \
  255.                                                              \
  256. AVFilter ff_vf_##name_ = {                                   \
  257.     .name          = #name_,                                 \
  258.     .description   = NULL_IF_CONFIG_SMALL(description_),     \
  259.     .priv_size     = sizeof(NContext),                       \
  260.     .priv_class    = &name_##_class,                         \
  261.     .uninit        = uninit,                                 \
  262.     .query_formats = query_formats,                          \
  263.     .inputs        = neighbor_inputs,                        \
  264.     .outputs       = neighbor_outputs,                       \
  265.     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, \
  266. }
  267.  
  268. #if CONFIG_EROSION_FILTER
  269.  
  270. static const AVOption erosion_options[] = {
  271.     { "threshold0",  "set threshold for 1st plane",   OFFSET(threshold[0]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  272.     { "threshold1",  "set threshold for 2nd plane",   OFFSET(threshold[1]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  273.     { "threshold2",  "set threshold for 3rd plane",   OFFSET(threshold[2]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  274.     { "threshold3",  "set threshold for 4th plane",   OFFSET(threshold[3]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  275.     { "coordinates", "set coordinates",               OFFSET(coordinates),    AV_OPT_TYPE_INT, {.i64=255},   0, 255,   FLAGS },
  276.     { NULL }
  277. };
  278.  
  279. DEFINE_NEIGHBOR_FILTER(erosion, "Apply erosion effect");
  280.  
  281. #endif /* CONFIG_EROSION_FILTER */
  282.  
  283. #if CONFIG_DILATION_FILTER
  284.  
  285. static const AVOption dilation_options[] = {
  286.     { "threshold0",  "set threshold for 1st plane",   OFFSET(threshold[0]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  287.     { "threshold1",  "set threshold for 2nd plane",   OFFSET(threshold[1]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  288.     { "threshold2",  "set threshold for 3rd plane",   OFFSET(threshold[2]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  289.     { "threshold3",  "set threshold for 4th plane",   OFFSET(threshold[3]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  290.     { "coordinates", "set coordinates",               OFFSET(coordinates),    AV_OPT_TYPE_INT, {.i64=255},   0, 255,   FLAGS },
  291.     { NULL }
  292. };
  293.  
  294. DEFINE_NEIGHBOR_FILTER(dilation, "Apply dilation effect");
  295.  
  296. #endif /* CONFIG_DILATION_FILTER */
  297.  
  298. #if CONFIG_DEFLATE_FILTER
  299.  
  300. static const AVOption deflate_options[] = {
  301.     { "threshold0", "set threshold for 1st plane",   OFFSET(threshold[0]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  302.     { "threshold1", "set threshold for 2nd plane",   OFFSET(threshold[1]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  303.     { "threshold2", "set threshold for 3rd plane",   OFFSET(threshold[2]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  304.     { "threshold3", "set threshold for 4th plane",   OFFSET(threshold[3]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  305.     { NULL }
  306. };
  307.  
  308. DEFINE_NEIGHBOR_FILTER(deflate, "Apply deflate effect");
  309.  
  310. #endif /* CONFIG_DEFLATE_FILTER */
  311.  
  312. #if CONFIG_INFLATE_FILTER
  313.  
  314. static const AVOption inflate_options[] = {
  315.     { "threshold0", "set threshold for 1st plane",   OFFSET(threshold[0]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  316.     { "threshold1", "set threshold for 2nd plane",   OFFSET(threshold[1]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  317.     { "threshold2", "set threshold for 3rd plane",   OFFSET(threshold[2]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  318.     { "threshold3", "set threshold for 4th plane",   OFFSET(threshold[3]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  319.     { NULL }
  320. };
  321.  
  322. DEFINE_NEIGHBOR_FILTER(inflate, "Apply inflate effect");
  323.  
  324. #endif /* CONFIG_INFLATE_FILTER */
  325.