Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright (c) 2013 Paul B Mahol
  3.  *
  4.  * This file is part of FFmpeg.
  5.  *
  6.  * FFmpeg is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2.1 of the License, or (at your option) any later version.
  10.  *
  11.  * FFmpeg is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with FFmpeg; if not, write to the Free Software
  18.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19.  */
  20.  
  21. #include "libavutil/imgutils.h"
  22. #include "libavutil/opt.h"
  23. #include "libavutil/pixdesc.h"
  24. #include "avfilter.h"
  25. #include "drawutils.h"
  26. #include "formats.h"
  27. #include "internal.h"
  28. #include "video.h"
  29.  
  30. #define R 0
  31. #define G 1
  32. #define B 2
  33. #define A 3
  34.  
  35. typedef struct {
  36.     double in_min, in_max;
  37.     double out_min, out_max;
  38. } Range;
  39.  
  40. typedef struct {
  41.     const AVClass *class;
  42.     Range range[4];
  43.     int nb_comp;
  44.     int bpp;
  45.     int step;
  46.     uint8_t rgba_map[4];
  47.     int linesize;
  48. } ColorLevelsContext;
  49.  
  50. #define OFFSET(x) offsetof(ColorLevelsContext, x)
  51. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  52. static const AVOption colorlevels_options[] = {
  53.     { "rimin", "set input red black point",    OFFSET(range[R].in_min),  AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  54.     { "gimin", "set input green black point",  OFFSET(range[G].in_min),  AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  55.     { "bimin", "set input blue black point",   OFFSET(range[B].in_min),  AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  56.     { "aimin", "set input alpha black point",  OFFSET(range[A].in_min),  AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  57.     { "rimax", "set input red white point",    OFFSET(range[R].in_max),  AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, FLAGS },
  58.     { "gimax", "set input green white point",  OFFSET(range[G].in_max),  AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, FLAGS },
  59.     { "bimax", "set input blue white point",   OFFSET(range[B].in_max),  AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, FLAGS },
  60.     { "aimax", "set input alpha white point",  OFFSET(range[A].in_max),  AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, FLAGS },
  61.     { "romin", "set output red black point",   OFFSET(range[R].out_min), AV_OPT_TYPE_DOUBLE, {.dbl=0},  0, 1, FLAGS },
  62.     { "gomin", "set output green black point", OFFSET(range[G].out_min), AV_OPT_TYPE_DOUBLE, {.dbl=0},  0, 1, FLAGS },
  63.     { "bomin", "set output blue black point",  OFFSET(range[B].out_min), AV_OPT_TYPE_DOUBLE, {.dbl=0},  0, 1, FLAGS },
  64.     { "aomin", "set output alpha black point", OFFSET(range[A].out_min), AV_OPT_TYPE_DOUBLE, {.dbl=0},  0, 1, FLAGS },
  65.     { "romax", "set output red white point",   OFFSET(range[R].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1},  0, 1, FLAGS },
  66.     { "gomax", "set output green white point", OFFSET(range[G].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1},  0, 1, FLAGS },
  67.     { "bomax", "set output blue white point",  OFFSET(range[B].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1},  0, 1, FLAGS },
  68.     { "aomax", "set output alpha white point", OFFSET(range[A].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1},  0, 1, FLAGS },
  69.     { NULL }
  70. };
  71.  
  72. AVFILTER_DEFINE_CLASS(colorlevels);
  73.  
  74. static int query_formats(AVFilterContext *ctx)
  75. {
  76.     static const enum AVPixelFormat pix_fmts[] = {
  77.         AV_PIX_FMT_0RGB,  AV_PIX_FMT_0BGR,
  78.         AV_PIX_FMT_ARGB,  AV_PIX_FMT_ABGR,
  79.         AV_PIX_FMT_RGB0,  AV_PIX_FMT_BGR0,
  80.         AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
  81.         AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48,
  82.         AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
  83.         AV_PIX_FMT_RGBA,  AV_PIX_FMT_BGRA,
  84.         AV_PIX_FMT_NONE
  85.     };
  86.  
  87.     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
  88.     if (!fmts_list)
  89.         return AVERROR(ENOMEM);
  90.     return ff_set_common_formats(ctx, fmts_list);
  91. }
  92.  
  93. static int config_input(AVFilterLink *inlink)
  94. {
  95.     AVFilterContext *ctx = inlink->dst;
  96.     ColorLevelsContext *s = ctx->priv;
  97.     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  98.  
  99.     s->nb_comp = desc->nb_components;
  100.     s->bpp = (desc->comp[0].depth_minus1 + 1) >> 3;
  101.     s->step = (av_get_padded_bits_per_pixel(desc) >> 3) / s->bpp;
  102.     s->linesize = inlink->w * s->step;
  103.     ff_fill_rgba_map(s->rgba_map, inlink->format);
  104.  
  105.     return 0;
  106. }
  107.  
  108. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  109. {
  110.     AVFilterContext *ctx = inlink->dst;
  111.     ColorLevelsContext *s = ctx->priv;
  112.     AVFilterLink *outlink = ctx->outputs[0];
  113.     const int step = s->step;
  114.     AVFrame *out;
  115.     int x, y, i;
  116.  
  117.     if (av_frame_is_writable(in)) {
  118.         out = in;
  119.     } else {
  120.         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  121.         if (!out) {
  122.             av_frame_free(&in);
  123.             return AVERROR(ENOMEM);
  124.         }
  125.         av_frame_copy_props(out, in);
  126.     }
  127.  
  128.     switch (s->bpp) {
  129.     case 1:
  130.         for (i = 0; i < s->nb_comp; i++) {
  131.             Range *r = &s->range[i];
  132.             const uint8_t offset = s->rgba_map[i];
  133.             const uint8_t *srcrow = in->data[0];
  134.             uint8_t *dstrow = out->data[0];
  135.             int imin = round(r->in_min  * UINT8_MAX);
  136.             int imax = round(r->in_max  * UINT8_MAX);
  137.             int omin = round(r->out_min * UINT8_MAX);
  138.             int omax = round(r->out_max * UINT8_MAX);
  139.             double coeff;
  140.  
  141.             if (imin < 0) {
  142.                 imin = UINT8_MAX;
  143.                 for (y = 0; y < inlink->h; y++) {
  144.                     const uint8_t *src = srcrow;
  145.  
  146.                     for (x = 0; x < s->linesize; x += step)
  147.                         imin = FFMIN(imin, src[x + offset]);
  148.                     srcrow += in->linesize[0];
  149.                 }
  150.             }
  151.             if (imax < 0) {
  152.                 srcrow = in->data[0];
  153.                 imax = 0;
  154.                 for (y = 0; y < inlink->h; y++) {
  155.                     const uint8_t *src = srcrow;
  156.  
  157.                     for (x = 0; x < s->linesize; x += step)
  158.                         imax = FFMAX(imax, src[x + offset]);
  159.                     srcrow += in->linesize[0];
  160.                 }
  161.             }
  162.  
  163.             srcrow = in->data[0];
  164.             coeff = (omax - omin) / (double)(imax - imin);
  165.             for (y = 0; y < inlink->h; y++) {
  166.                 const uint8_t *src = srcrow;
  167.                 uint8_t *dst = dstrow;
  168.  
  169.                 for (x = 0; x < s->linesize; x += step)
  170.                     dst[x + offset] = av_clip_uint8((src[x + offset] - imin) * coeff + omin);
  171.                 dstrow += out->linesize[0];
  172.                 srcrow += in->linesize[0];
  173.             }
  174.         }
  175.         break;
  176.     case 2:
  177.         for (i = 0; i < s->nb_comp; i++) {
  178.             Range *r = &s->range[i];
  179.             const uint8_t offset = s->rgba_map[i];
  180.             const uint8_t *srcrow = in->data[0];
  181.             uint8_t *dstrow = out->data[0];
  182.             int imin = round(r->in_min  * UINT16_MAX);
  183.             int imax = round(r->in_max  * UINT16_MAX);
  184.             int omin = round(r->out_min * UINT16_MAX);
  185.             int omax = round(r->out_max * UINT16_MAX);
  186.             double coeff;
  187.  
  188.             if (imin < 0) {
  189.                 imin = UINT16_MAX;
  190.                 for (y = 0; y < inlink->h; y++) {
  191.                     const uint16_t *src = (const uint16_t *)srcrow;
  192.  
  193.                     for (x = 0; x < s->linesize; x += step)
  194.                         imin = FFMIN(imin, src[x + offset]);
  195.                     srcrow += in->linesize[0];
  196.                 }
  197.             }
  198.             if (imax < 0) {
  199.                 srcrow = in->data[0];
  200.                 imax = 0;
  201.                 for (y = 0; y < inlink->h; y++) {
  202.                     const uint16_t *src = (const uint16_t *)srcrow;
  203.  
  204.                     for (x = 0; x < s->linesize; x += step)
  205.                         imax = FFMAX(imax, src[x + offset]);
  206.                     srcrow += in->linesize[0];
  207.                 }
  208.             }
  209.  
  210.             srcrow = in->data[0];
  211.             coeff = (omax - omin) / (double)(imax - imin);
  212.             for (y = 0; y < inlink->h; y++) {
  213.                 const uint16_t *src = (const uint16_t*)srcrow;
  214.                 uint16_t *dst = (uint16_t *)dstrow;
  215.  
  216.                 for (x = 0; x < s->linesize; x += step)
  217.                     dst[x + offset] = av_clip_uint16((src[x + offset] - imin) * coeff + omin);
  218.                 dstrow += out->linesize[0];
  219.                 srcrow += in->linesize[0];
  220.             }
  221.         }
  222.     }
  223.  
  224.     if (in != out)
  225.         av_frame_free(&in);
  226.     return ff_filter_frame(outlink, out);
  227. }
  228.  
  229. static const AVFilterPad colorlevels_inputs[] = {
  230.     {
  231.         .name         = "default",
  232.         .type         = AVMEDIA_TYPE_VIDEO,
  233.         .filter_frame = filter_frame,
  234.         .config_props = config_input,
  235.     },
  236.     { NULL }
  237. };
  238.  
  239. static const AVFilterPad colorlevels_outputs[] = {
  240.     {
  241.         .name = "default",
  242.         .type = AVMEDIA_TYPE_VIDEO,
  243.     },
  244.     { NULL }
  245. };
  246.  
  247. AVFilter ff_vf_colorlevels = {
  248.     .name          = "colorlevels",
  249.     .description   = NULL_IF_CONFIG_SMALL("Adjust the color levels."),
  250.     .priv_size     = sizeof(ColorLevelsContext),
  251.     .priv_class    = &colorlevels_class,
  252.     .query_formats = query_formats,
  253.     .inputs        = colorlevels_inputs,
  254.     .outputs       = colorlevels_outputs,
  255.     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
  256. };
  257.