Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6147 | serge | 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 | };>>>>>>>>>>>>>>>>>> |