yading@10
|
1 /*
|
yading@10
|
2 * Copyright (c) 2013 Clément Bœsch
|
yading@10
|
3 *
|
yading@10
|
4 * This file is part of FFmpeg.
|
yading@10
|
5 *
|
yading@10
|
6 * FFmpeg is free software; you can redistribute it and/or
|
yading@10
|
7 * modify it under the terms of the GNU Lesser General Public
|
yading@10
|
8 * License as published by the Free Software Foundation; either
|
yading@10
|
9 * version 2.1 of the License, or (at your option) any later version.
|
yading@10
|
10 *
|
yading@10
|
11 * FFmpeg is distributed in the hope that it will be useful,
|
yading@10
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@10
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
yading@10
|
14 * Lesser General Public License for more details.
|
yading@10
|
15 *
|
yading@10
|
16 * You should have received a copy of the GNU Lesser General Public
|
yading@10
|
17 * License along with FFmpeg; if not, write to the Free Software
|
yading@10
|
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@10
|
19 */
|
yading@10
|
20
|
yading@10
|
21 #include "libavutil/opt.h"
|
yading@10
|
22 #include "libavutil/bprint.h"
|
yading@10
|
23 #include "libavutil/eval.h"
|
yading@10
|
24 #include "libavutil/file.h"
|
yading@10
|
25 #include "libavutil/intreadwrite.h"
|
yading@10
|
26 #include "libavutil/avassert.h"
|
yading@10
|
27 #include "avfilter.h"
|
yading@10
|
28 #include "formats.h"
|
yading@10
|
29 #include "internal.h"
|
yading@10
|
30 #include "video.h"
|
yading@10
|
31
|
yading@10
|
32 struct keypoint {
|
yading@10
|
33 double x, y;
|
yading@10
|
34 struct keypoint *next;
|
yading@10
|
35 };
|
yading@10
|
36
|
yading@10
|
37 #define NB_COMP 3
|
yading@10
|
38
|
yading@10
|
39 enum preset {
|
yading@10
|
40 PRESET_NONE,
|
yading@10
|
41 PRESET_COLOR_NEGATIVE,
|
yading@10
|
42 PRESET_CROSS_PROCESS,
|
yading@10
|
43 PRESET_DARKER,
|
yading@10
|
44 PRESET_INCREASE_CONTRAST,
|
yading@10
|
45 PRESET_LIGHTER,
|
yading@10
|
46 PRESET_LINEAR_CONTRAST,
|
yading@10
|
47 PRESET_MEDIUM_CONTRAST,
|
yading@10
|
48 PRESET_NEGATIVE,
|
yading@10
|
49 PRESET_STRONG_CONTRAST,
|
yading@10
|
50 PRESET_VINTAGE,
|
yading@10
|
51 NB_PRESETS,
|
yading@10
|
52 };
|
yading@10
|
53
|
yading@10
|
54 typedef struct {
|
yading@10
|
55 const AVClass *class;
|
yading@10
|
56 enum preset preset;
|
yading@10
|
57 char *comp_points_str[NB_COMP + 1];
|
yading@10
|
58 char *comp_points_str_all;
|
yading@10
|
59 uint8_t graph[NB_COMP + 1][256];
|
yading@10
|
60 char *psfile;
|
yading@10
|
61 } CurvesContext;
|
yading@10
|
62
|
yading@10
|
63 #define OFFSET(x) offsetof(CurvesContext, x)
|
yading@10
|
64 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
|
yading@10
|
65 static const AVOption curves_options[] = {
|
yading@10
|
66 { "preset", "select a color curves preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=PRESET_NONE}, PRESET_NONE, NB_PRESETS-1, FLAGS, "preset_name" },
|
yading@10
|
67 { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NONE}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
68 { "color_negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COLOR_NEGATIVE}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
69 { "cross_process", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CROSS_PROCESS}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
70 { "darker", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_DARKER}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
71 { "increase_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INCREASE_CONTRAST}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
72 { "lighter", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LIGHTER}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
73 { "linear_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LINEAR_CONTRAST}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
74 { "medium_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MEDIUM_CONTRAST}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
75 { "negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NEGATIVE}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
76 { "strong_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_STRONG_CONTRAST}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
77 { "vintage", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VINTAGE}, INT_MIN, INT_MAX, FLAGS, "preset_name" },
|
yading@10
|
78 { "master","set master points coordinates",OFFSET(comp_points_str[NB_COMP]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
79 { "m", "set master points coordinates",OFFSET(comp_points_str[NB_COMP]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
80 { "red", "set red points coordinates", OFFSET(comp_points_str[0]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
81 { "r", "set red points coordinates", OFFSET(comp_points_str[0]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
82 { "green", "set green points coordinates", OFFSET(comp_points_str[1]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
83 { "g", "set green points coordinates", OFFSET(comp_points_str[1]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
84 { "blue", "set blue points coordinates", OFFSET(comp_points_str[2]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
85 { "b", "set blue points coordinates", OFFSET(comp_points_str[2]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
86 { "all", "set points coordinates for all components", OFFSET(comp_points_str_all), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
87 { "psfile", "set Photoshop curves file name", OFFSET(psfile), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
yading@10
|
88 { NULL }
|
yading@10
|
89 };
|
yading@10
|
90
|
yading@10
|
91 AVFILTER_DEFINE_CLASS(curves);
|
yading@10
|
92
|
yading@10
|
93 static const struct {
|
yading@10
|
94 const char *r;
|
yading@10
|
95 const char *g;
|
yading@10
|
96 const char *b;
|
yading@10
|
97 const char *master;
|
yading@10
|
98 } curves_presets[] = {
|
yading@10
|
99 [PRESET_COLOR_NEGATIVE] = {
|
yading@10
|
100 "0/1 0.129/1 0.466/0.498 0.725/0 1/0",
|
yading@10
|
101 "0/1 0.109/1 0.301/0.498 0.517/0 1/0",
|
yading@10
|
102 "0/1 0.098/1 0.235/0.498 0.423/0 1/0",
|
yading@10
|
103 },
|
yading@10
|
104 [PRESET_CROSS_PROCESS] = {
|
yading@10
|
105 "0.25/0.156 0.501/0.501 0.686/0.745",
|
yading@10
|
106 "0.25/0.188 0.38/0.501 0.745/0.815 1/0.815",
|
yading@10
|
107 "0.231/0.094 0.709/0.874",
|
yading@10
|
108 },
|
yading@10
|
109 [PRESET_DARKER] = { .master = "0.5/0.4" },
|
yading@10
|
110 [PRESET_INCREASE_CONTRAST] = { .master = "0.149/0.066 0.831/0.905 0.905/0.98" },
|
yading@10
|
111 [PRESET_LIGHTER] = { .master = "0.4/0.5" },
|
yading@10
|
112 [PRESET_LINEAR_CONTRAST] = { .master = "0.305/0.286 0.694/0.713" },
|
yading@10
|
113 [PRESET_MEDIUM_CONTRAST] = { .master = "0.286/0.219 0.639/0.643" },
|
yading@10
|
114 [PRESET_NEGATIVE] = { .master = "0/1 1/0" },
|
yading@10
|
115 [PRESET_STRONG_CONTRAST] = { .master = "0.301/0.196 0.592/0.6 0.686/0.737" },
|
yading@10
|
116 [PRESET_VINTAGE] = {
|
yading@10
|
117 "0/0.11 0.42/0.51 1/0.95",
|
yading@10
|
118 "0.50/0.48",
|
yading@10
|
119 "0/0.22 0.49/0.44 1/0.8",
|
yading@10
|
120 }
|
yading@10
|
121 };
|
yading@10
|
122
|
yading@10
|
123 static struct keypoint *make_point(double x, double y, struct keypoint *next)
|
yading@10
|
124 {
|
yading@10
|
125 struct keypoint *point = av_mallocz(sizeof(*point));
|
yading@10
|
126
|
yading@10
|
127 if (!point)
|
yading@10
|
128 return NULL;
|
yading@10
|
129 point->x = x;
|
yading@10
|
130 point->y = y;
|
yading@10
|
131 point->next = next;
|
yading@10
|
132 return point;
|
yading@10
|
133 }
|
yading@10
|
134
|
yading@10
|
135 static int parse_points_str(AVFilterContext *ctx, struct keypoint **points, const char *s)
|
yading@10
|
136 {
|
yading@10
|
137 char *p = (char *)s; // strtod won't alter the string
|
yading@10
|
138 struct keypoint *last = NULL;
|
yading@10
|
139
|
yading@10
|
140 /* construct a linked list based on the key points string */
|
yading@10
|
141 while (p && *p) {
|
yading@10
|
142 struct keypoint *point = make_point(0, 0, NULL);
|
yading@10
|
143 if (!point)
|
yading@10
|
144 return AVERROR(ENOMEM);
|
yading@10
|
145 point->x = av_strtod(p, &p); if (p && *p) p++;
|
yading@10
|
146 point->y = av_strtod(p, &p); if (p && *p) p++;
|
yading@10
|
147 if (point->x < 0 || point->x > 1 || point->y < 0 || point->y > 1) {
|
yading@10
|
148 av_log(ctx, AV_LOG_ERROR, "Invalid key point coordinates (%f;%f), "
|
yading@10
|
149 "x and y must be in the [0;1] range.\n", point->x, point->y);
|
yading@10
|
150 return AVERROR(EINVAL);
|
yading@10
|
151 }
|
yading@10
|
152 if (!*points)
|
yading@10
|
153 *points = point;
|
yading@10
|
154 if (last) {
|
yading@10
|
155 if ((int)(last->x * 255) >= (int)(point->x * 255)) {
|
yading@10
|
156 av_log(ctx, AV_LOG_ERROR, "Key point coordinates (%f;%f) "
|
yading@10
|
157 "and (%f;%f) are too close from each other or not "
|
yading@10
|
158 "strictly increasing on the x-axis\n",
|
yading@10
|
159 last->x, last->y, point->x, point->y);
|
yading@10
|
160 return AVERROR(EINVAL);
|
yading@10
|
161 }
|
yading@10
|
162 last->next = point;
|
yading@10
|
163 }
|
yading@10
|
164 last = point;
|
yading@10
|
165 }
|
yading@10
|
166
|
yading@10
|
167 /* auto insert first key point if missing at x=0 */
|
yading@10
|
168 if (!*points) {
|
yading@10
|
169 last = make_point(0, 0, NULL);
|
yading@10
|
170 if (!last)
|
yading@10
|
171 return AVERROR(ENOMEM);
|
yading@10
|
172 last->x = last->y = 0;
|
yading@10
|
173 *points = last;
|
yading@10
|
174 } else if ((*points)->x != 0.) {
|
yading@10
|
175 struct keypoint *newfirst = make_point(0, 0, *points);
|
yading@10
|
176 if (!newfirst)
|
yading@10
|
177 return AVERROR(ENOMEM);
|
yading@10
|
178 *points = newfirst;
|
yading@10
|
179 }
|
yading@10
|
180
|
yading@10
|
181 av_assert0(last);
|
yading@10
|
182
|
yading@10
|
183 /* auto insert last key point if missing at x=1 */
|
yading@10
|
184 if (last->x != 1.) {
|
yading@10
|
185 struct keypoint *point = make_point(1, 1, NULL);
|
yading@10
|
186 if (!point)
|
yading@10
|
187 return AVERROR(ENOMEM);
|
yading@10
|
188 last->next = point;
|
yading@10
|
189 }
|
yading@10
|
190
|
yading@10
|
191 return 0;
|
yading@10
|
192 }
|
yading@10
|
193
|
yading@10
|
194 static int get_nb_points(const struct keypoint *d)
|
yading@10
|
195 {
|
yading@10
|
196 int n = 0;
|
yading@10
|
197 while (d) {
|
yading@10
|
198 n++;
|
yading@10
|
199 d = d->next;
|
yading@10
|
200 }
|
yading@10
|
201 return n;
|
yading@10
|
202 }
|
yading@10
|
203
|
yading@10
|
204 /**
|
yading@10
|
205 * Natural cubic spline interpolation
|
yading@10
|
206 * Finding curves using Cubic Splines notes by Steven Rauch and John Stockie.
|
yading@10
|
207 * @see http://people.math.sfu.ca/~stockie/teaching/macm316/notes/splines.pdf
|
yading@10
|
208 */
|
yading@10
|
209 static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint *points)
|
yading@10
|
210 {
|
yading@10
|
211 int i, ret = 0;
|
yading@10
|
212 const struct keypoint *point;
|
yading@10
|
213 double xprev = 0;
|
yading@10
|
214
|
yading@10
|
215 int n = get_nb_points(points); // number of splines
|
yading@10
|
216
|
yading@10
|
217 double (*matrix)[3] = av_calloc(n, sizeof(*matrix));
|
yading@10
|
218 double *h = av_malloc((n - 1) * sizeof(*h));
|
yading@10
|
219 double *r = av_calloc(n, sizeof(*r));
|
yading@10
|
220
|
yading@10
|
221 if (!matrix || !h || !r) {
|
yading@10
|
222 ret = AVERROR(ENOMEM);
|
yading@10
|
223 goto end;
|
yading@10
|
224 }
|
yading@10
|
225
|
yading@10
|
226 /* h(i) = x(i+1) - x(i) */
|
yading@10
|
227 i = -1;
|
yading@10
|
228 for (point = points; point; point = point->next) {
|
yading@10
|
229 if (i != -1)
|
yading@10
|
230 h[i] = point->x - xprev;
|
yading@10
|
231 xprev = point->x;
|
yading@10
|
232 i++;
|
yading@10
|
233 }
|
yading@10
|
234
|
yading@10
|
235 /* right-side of the polynomials, will be modified to contains the solution */
|
yading@10
|
236 point = points;
|
yading@10
|
237 for (i = 1; i < n - 1; i++) {
|
yading@10
|
238 double yp = point->y,
|
yading@10
|
239 yc = point->next->y,
|
yading@10
|
240 yn = point->next->next->y;
|
yading@10
|
241 r[i] = 6 * ((yn-yc)/h[i] - (yc-yp)/h[i-1]);
|
yading@10
|
242 point = point->next;
|
yading@10
|
243 }
|
yading@10
|
244
|
yading@10
|
245 #define B 0 /* sub diagonal (below main) */
|
yading@10
|
246 #define M 1 /* main diagonal (center) */
|
yading@10
|
247 #define A 2 /* sup diagonal (above main) */
|
yading@10
|
248
|
yading@10
|
249 /* left side of the polynomials into a tridiagonal matrix. */
|
yading@10
|
250 matrix[0][M] = matrix[n - 1][M] = 1;
|
yading@10
|
251 for (i = 1; i < n - 1; i++) {
|
yading@10
|
252 matrix[i][B] = h[i-1];
|
yading@10
|
253 matrix[i][M] = 2 * (h[i-1] + h[i]);
|
yading@10
|
254 matrix[i][A] = h[i];
|
yading@10
|
255 }
|
yading@10
|
256
|
yading@10
|
257 /* tridiagonal solving of the linear system */
|
yading@10
|
258 for (i = 1; i < n; i++) {
|
yading@10
|
259 double den = matrix[i][M] - matrix[i][B] * matrix[i-1][A];
|
yading@10
|
260 double k = den ? 1./den : 1.;
|
yading@10
|
261 matrix[i][A] *= k;
|
yading@10
|
262 r[i] = (r[i] - matrix[i][B] * r[i - 1]) * k;
|
yading@10
|
263 }
|
yading@10
|
264 for (i = n - 2; i >= 0; i--)
|
yading@10
|
265 r[i] = r[i] - matrix[i][A] * r[i + 1];
|
yading@10
|
266
|
yading@10
|
267 /* compute the graph with x=[0..255] */
|
yading@10
|
268 i = 0;
|
yading@10
|
269 point = points;
|
yading@10
|
270 av_assert0(point->next); // always at least 2 key points
|
yading@10
|
271 while (point->next) {
|
yading@10
|
272 double yc = point->y;
|
yading@10
|
273 double yn = point->next->y;
|
yading@10
|
274
|
yading@10
|
275 double a = yc;
|
yading@10
|
276 double b = (yn-yc)/h[i] - h[i]*r[i]/2. - h[i]*(r[i+1]-r[i])/6.;
|
yading@10
|
277 double c = r[i] / 2.;
|
yading@10
|
278 double d = (r[i+1] - r[i]) / (6.*h[i]);
|
yading@10
|
279
|
yading@10
|
280 int x;
|
yading@10
|
281 int x_start = point->x * 255;
|
yading@10
|
282 int x_end = point->next->x * 255;
|
yading@10
|
283
|
yading@10
|
284 av_assert0(x_start >= 0 && x_start <= 255 &&
|
yading@10
|
285 x_end >= 0 && x_end <= 255);
|
yading@10
|
286
|
yading@10
|
287 for (x = x_start; x <= x_end; x++) {
|
yading@10
|
288 double xx = (x - x_start) * 1/255.;
|
yading@10
|
289 double yy = a + b*xx + c*xx*xx + d*xx*xx*xx;
|
yading@10
|
290 y[x] = av_clipf(yy, 0, 1) * 255;
|
yading@10
|
291 av_log(ctx, AV_LOG_DEBUG, "f(%f)=%f -> y[%d]=%d\n", xx, yy, x, y[x]);
|
yading@10
|
292 }
|
yading@10
|
293
|
yading@10
|
294 point = point->next;
|
yading@10
|
295 i++;
|
yading@10
|
296 }
|
yading@10
|
297
|
yading@10
|
298 end:
|
yading@10
|
299 av_free(matrix);
|
yading@10
|
300 av_free(h);
|
yading@10
|
301 av_free(r);
|
yading@10
|
302 return ret;
|
yading@10
|
303 }
|
yading@10
|
304
|
yading@10
|
305 static int parse_psfile(AVFilterContext *ctx, const char *fname)
|
yading@10
|
306 {
|
yading@10
|
307 CurvesContext *curves = ctx->priv;
|
yading@10
|
308 uint8_t *buf;
|
yading@10
|
309 size_t size;
|
yading@10
|
310 int i, ret, av_unused(version), nb_curves;
|
yading@10
|
311 AVBPrint ptstr;
|
yading@10
|
312 static const int comp_ids[] = {3, 0, 1, 2};
|
yading@10
|
313
|
yading@10
|
314 av_bprint_init(&ptstr, 0, AV_BPRINT_SIZE_AUTOMATIC);
|
yading@10
|
315
|
yading@10
|
316 ret = av_file_map(fname, &buf, &size, 0, NULL);
|
yading@10
|
317 if (ret < 0)
|
yading@10
|
318 return ret;
|
yading@10
|
319
|
yading@10
|
320 #define READ16(dst) do { \
|
yading@10
|
321 if (size < 2) \
|
yading@10
|
322 return AVERROR_INVALIDDATA; \
|
yading@10
|
323 dst = AV_RB16(buf); \
|
yading@10
|
324 buf += 2; \
|
yading@10
|
325 size -= 2; \
|
yading@10
|
326 } while (0)
|
yading@10
|
327
|
yading@10
|
328 READ16(version);
|
yading@10
|
329 READ16(nb_curves);
|
yading@10
|
330 for (i = 0; i < FFMIN(nb_curves, FF_ARRAY_ELEMS(comp_ids)); i++) {
|
yading@10
|
331 int nb_points, n;
|
yading@10
|
332 av_bprint_clear(&ptstr);
|
yading@10
|
333 READ16(nb_points);
|
yading@10
|
334 for (n = 0; n < nb_points; n++) {
|
yading@10
|
335 int y, x;
|
yading@10
|
336 READ16(y);
|
yading@10
|
337 READ16(x);
|
yading@10
|
338 av_bprintf(&ptstr, "%f/%f ", x / 255., y / 255.);
|
yading@10
|
339 }
|
yading@10
|
340 if (*ptstr.str) {
|
yading@10
|
341 char **pts = &curves->comp_points_str[comp_ids[i]];
|
yading@10
|
342 if (!*pts) {
|
yading@10
|
343 *pts = av_strdup(ptstr.str);
|
yading@10
|
344 av_log(ctx, AV_LOG_DEBUG, "curves %d (intid=%d) [%d points]: [%s]\n",
|
yading@10
|
345 i, comp_ids[i], nb_points, *pts);
|
yading@10
|
346 if (!*pts) {
|
yading@10
|
347 ret = AVERROR(ENOMEM);
|
yading@10
|
348 goto end;
|
yading@10
|
349 }
|
yading@10
|
350 }
|
yading@10
|
351 }
|
yading@10
|
352 }
|
yading@10
|
353 end:
|
yading@10
|
354 av_bprint_finalize(&ptstr, NULL);
|
yading@10
|
355 av_file_unmap(buf, size);
|
yading@10
|
356 return ret;
|
yading@10
|
357 }
|
yading@10
|
358
|
yading@10
|
359 static av_cold int init(AVFilterContext *ctx)
|
yading@10
|
360 {
|
yading@10
|
361 int i, j, ret;
|
yading@10
|
362 CurvesContext *curves = ctx->priv;
|
yading@10
|
363 struct keypoint *comp_points[NB_COMP + 1] = {0};
|
yading@10
|
364 char **pts = curves->comp_points_str;
|
yading@10
|
365 const char *allp = curves->comp_points_str_all;
|
yading@10
|
366
|
yading@10
|
367 //if (!allp && curves->preset != PRESET_NONE && curves_presets[curves->preset].all)
|
yading@10
|
368 // allp = curves_presets[curves->preset].all;
|
yading@10
|
369
|
yading@10
|
370 if (allp) {
|
yading@10
|
371 for (i = 0; i < NB_COMP; i++) {
|
yading@10
|
372 if (!pts[i])
|
yading@10
|
373 pts[i] = av_strdup(allp);
|
yading@10
|
374 if (!pts[i])
|
yading@10
|
375 return AVERROR(ENOMEM);
|
yading@10
|
376 }
|
yading@10
|
377 }
|
yading@10
|
378
|
yading@10
|
379 if (curves->psfile) {
|
yading@10
|
380 ret = parse_psfile(ctx, curves->psfile);
|
yading@10
|
381 if (ret < 0)
|
yading@10
|
382 return ret;
|
yading@10
|
383 }
|
yading@10
|
384
|
yading@10
|
385 if (curves->preset != PRESET_NONE) {
|
yading@10
|
386 #define SET_COMP_IF_NOT_SET(n, name) do { \
|
yading@10
|
387 if (!pts[n] && curves_presets[curves->preset].name) { \
|
yading@10
|
388 pts[n] = av_strdup(curves_presets[curves->preset].name); \
|
yading@10
|
389 if (!pts[n]) \
|
yading@10
|
390 return AVERROR(ENOMEM); \
|
yading@10
|
391 } \
|
yading@10
|
392 } while (0)
|
yading@10
|
393 SET_COMP_IF_NOT_SET(0, r);
|
yading@10
|
394 SET_COMP_IF_NOT_SET(1, g);
|
yading@10
|
395 SET_COMP_IF_NOT_SET(2, b);
|
yading@10
|
396 SET_COMP_IF_NOT_SET(3, master);
|
yading@10
|
397 }
|
yading@10
|
398
|
yading@10
|
399 for (i = 0; i < NB_COMP + 1; i++) {
|
yading@10
|
400 ret = parse_points_str(ctx, comp_points + i, curves->comp_points_str[i]);
|
yading@10
|
401 if (ret < 0)
|
yading@10
|
402 return ret;
|
yading@10
|
403 ret = interpolate(ctx, curves->graph[i], comp_points[i]);
|
yading@10
|
404 if (ret < 0)
|
yading@10
|
405 return ret;
|
yading@10
|
406 }
|
yading@10
|
407
|
yading@10
|
408 if (pts[NB_COMP]) {
|
yading@10
|
409 for (i = 0; i < NB_COMP; i++)
|
yading@10
|
410 for (j = 0; j < 256; j++)
|
yading@10
|
411 curves->graph[i][j] = curves->graph[NB_COMP][curves->graph[i][j]];
|
yading@10
|
412 }
|
yading@10
|
413
|
yading@10
|
414 if (av_log_get_level() >= AV_LOG_VERBOSE) {
|
yading@10
|
415 for (i = 0; i < NB_COMP; i++) {
|
yading@10
|
416 struct keypoint *point = comp_points[i];
|
yading@10
|
417 av_log(ctx, AV_LOG_VERBOSE, "#%d points:", i);
|
yading@10
|
418 while (point) {
|
yading@10
|
419 av_log(ctx, AV_LOG_VERBOSE, " (%f;%f)", point->x, point->y);
|
yading@10
|
420 point = point->next;
|
yading@10
|
421 }
|
yading@10
|
422 av_log(ctx, AV_LOG_VERBOSE, "\n");
|
yading@10
|
423 av_log(ctx, AV_LOG_VERBOSE, "#%d values:", i);
|
yading@10
|
424 for (j = 0; j < 256; j++)
|
yading@10
|
425 av_log(ctx, AV_LOG_VERBOSE, " %02X", curves->graph[i][j]);
|
yading@10
|
426 av_log(ctx, AV_LOG_VERBOSE, "\n");
|
yading@10
|
427 }
|
yading@10
|
428 }
|
yading@10
|
429
|
yading@10
|
430 for (i = 0; i < NB_COMP + 1; i++) {
|
yading@10
|
431 struct keypoint *point = comp_points[i];
|
yading@10
|
432 while (point) {
|
yading@10
|
433 struct keypoint *next = point->next;
|
yading@10
|
434 av_free(point);
|
yading@10
|
435 point = next;
|
yading@10
|
436 }
|
yading@10
|
437 }
|
yading@10
|
438
|
yading@10
|
439 return 0;
|
yading@10
|
440 }
|
yading@10
|
441
|
yading@10
|
442 static int query_formats(AVFilterContext *ctx)
|
yading@10
|
443 {
|
yading@10
|
444 static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE};
|
yading@10
|
445 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
|
yading@10
|
446 return 0;
|
yading@10
|
447 }
|
yading@10
|
448
|
yading@10
|
449 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
|
yading@10
|
450 {
|
yading@10
|
451 int x, y, i, direct = 0;
|
yading@10
|
452 AVFilterContext *ctx = inlink->dst;
|
yading@10
|
453 CurvesContext *curves = ctx->priv;
|
yading@10
|
454 AVFilterLink *outlink = inlink->dst->outputs[0];
|
yading@10
|
455 AVFrame *out;
|
yading@10
|
456 uint8_t *dst;
|
yading@10
|
457 const uint8_t *src;
|
yading@10
|
458
|
yading@10
|
459 if (av_frame_is_writable(in)) {
|
yading@10
|
460 direct = 1;
|
yading@10
|
461 out = in;
|
yading@10
|
462 } else {
|
yading@10
|
463 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
|
yading@10
|
464 if (!out) {
|
yading@10
|
465 av_frame_free(&in);
|
yading@10
|
466 return AVERROR(ENOMEM);
|
yading@10
|
467 }
|
yading@10
|
468 av_frame_copy_props(out, in);
|
yading@10
|
469 }
|
yading@10
|
470
|
yading@10
|
471 dst = out->data[0];
|
yading@10
|
472 src = in ->data[0];
|
yading@10
|
473
|
yading@10
|
474 for (y = 0; y < inlink->h; y++) {
|
yading@10
|
475 uint8_t *dstp = dst;
|
yading@10
|
476 const uint8_t *srcp = src;
|
yading@10
|
477
|
yading@10
|
478 for (x = 0; x < inlink->w; x++)
|
yading@10
|
479 for (i = 0; i < NB_COMP; i++, dstp++, srcp++)
|
yading@10
|
480 *dstp = curves->graph[i][*srcp];
|
yading@10
|
481 dst += out->linesize[0];
|
yading@10
|
482 src += in ->linesize[0];
|
yading@10
|
483 }
|
yading@10
|
484
|
yading@10
|
485 if (!direct)
|
yading@10
|
486 av_frame_free(&in);
|
yading@10
|
487
|
yading@10
|
488 return ff_filter_frame(outlink, out);
|
yading@10
|
489 }
|
yading@10
|
490
|
yading@10
|
491 static const AVFilterPad curves_inputs[] = {
|
yading@10
|
492 {
|
yading@10
|
493 .name = "default",
|
yading@10
|
494 .type = AVMEDIA_TYPE_VIDEO,
|
yading@10
|
495 .filter_frame = filter_frame,
|
yading@10
|
496 },
|
yading@10
|
497 { NULL }
|
yading@10
|
498 };
|
yading@10
|
499
|
yading@10
|
500 static const AVFilterPad curves_outputs[] = {
|
yading@10
|
501 {
|
yading@10
|
502 .name = "default",
|
yading@10
|
503 .type = AVMEDIA_TYPE_VIDEO,
|
yading@10
|
504 },
|
yading@10
|
505 { NULL }
|
yading@10
|
506 };
|
yading@10
|
507
|
yading@10
|
508 AVFilter avfilter_vf_curves = {
|
yading@10
|
509 .name = "curves",
|
yading@10
|
510 .description = NULL_IF_CONFIG_SMALL("Adjust components curves."),
|
yading@10
|
511 .priv_size = sizeof(CurvesContext),
|
yading@10
|
512 .init = init,
|
yading@10
|
513 .query_formats = query_formats,
|
yading@10
|
514 .inputs = curves_inputs,
|
yading@10
|
515 .outputs = curves_outputs,
|
yading@10
|
516 .priv_class = &curves_class,
|
yading@10
|
517 };
|