Rename ColorEntry.rgba_color to rgb_color and do not reserve a fourth
[libav.git] / libavfilter / parseutils.c
1 /*
2 * copyright (c) 2009 Stefano Sabatini
3 * This file is part of FFmpeg.
4 *
5 * FFmpeg is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * FFmpeg is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with FFmpeg; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /**
21 * @file
22 * parsing utils
23 */
24
25 #include <strings.h>
26 #include "libavutil/avutil.h"
27 #include "libavutil/random_seed.h"
28 #include "parseutils.h"
29
30 #define WHITESPACES " \n\t"
31
32 char *av_get_token(const char **buf, const char *term)
33 {
34 char *out = av_malloc(strlen(*buf) + 1);
35 char *ret= out, *end= out;
36 const char *p = *buf;
37 if (!out) return NULL;
38 p += strspn(p, WHITESPACES);
39
40 while(*p && !strspn(p, term)) {
41 char c = *p++;
42 if(c == '\\' && *p){
43 *out++ = *p++;
44 end= out;
45 }else if(c == '\''){
46 while(*p && *p != '\'')
47 *out++ = *p++;
48 if(*p){
49 p++;
50 end= out;
51 }
52 }else{
53 *out++ = c;
54 }
55 }
56
57 do{
58 *out-- = 0;
59 }while(out >= end && strspn(out, WHITESPACES));
60
61 *buf = p;
62
63 return ret;
64 }
65
66 typedef struct {
67 const char *name; ///< a string representing the name of the color
68 uint8_t rgb_color[3]; ///< RGB values for the color
69 } ColorEntry;
70
71 static ColorEntry color_table[] = {
72 { "AliceBlue", { 0xF0, 0xF8, 0xFF } },
73 { "AntiqueWhite", { 0xFA, 0xEB, 0xD7 } },
74 { "Aqua", { 0x00, 0xFF, 0xFF } },
75 { "Aquamarine", { 0x7F, 0xFF, 0xD4 } },
76 { "Azure", { 0xF0, 0xFF, 0xFF } },
77 { "Beige", { 0xF5, 0xF5, 0xDC } },
78 { "Bisque", { 0xFF, 0xE4, 0xC4 } },
79 { "Black", { 0x00, 0x00, 0x00 } },
80 { "BlanchedAlmond", { 0xFF, 0xEB, 0xCD } },
81 { "Blue", { 0x00, 0x00, 0xFF } },
82 { "BlueViolet", { 0x8A, 0x2B, 0xE2 } },
83 { "Brown", { 0xA5, 0x2A, 0x2A } },
84 { "BurlyWood", { 0xDE, 0xB8, 0x87 } },
85 { "CadetBlue", { 0x5F, 0x9E, 0xA0 } },
86 { "Chartreuse", { 0x7F, 0xFF, 0x00 } },
87 { "Chocolate", { 0xD2, 0x69, 0x1E } },
88 { "Coral", { 0xFF, 0x7F, 0x50 } },
89 { "CornflowerBlue", { 0x64, 0x95, 0xED } },
90 { "Cornsilk", { 0xFF, 0xF8, 0xDC } },
91 { "Crimson", { 0xDC, 0x14, 0x3C } },
92 { "Cyan", { 0x00, 0xFF, 0xFF } },
93 { "DarkBlue", { 0x00, 0x00, 0x8B } },
94 { "DarkCyan", { 0x00, 0x8B, 0x8B } },
95 { "DarkGoldenRod", { 0xB8, 0x86, 0x0B } },
96 { "DarkGray", { 0xA9, 0xA9, 0xA9 } },
97 { "DarkGreen", { 0x00, 0x64, 0x00 } },
98 { "DarkKhaki", { 0xBD, 0xB7, 0x6B } },
99 { "DarkMagenta", { 0x8B, 0x00, 0x8B } },
100 { "DarkOliveGreen", { 0x55, 0x6B, 0x2F } },
101 { "Darkorange", { 0xFF, 0x8C, 0x00 } },
102 { "DarkOrchid", { 0x99, 0x32, 0xCC } },
103 { "DarkRed", { 0x8B, 0x00, 0x00 } },
104 { "DarkSalmon", { 0xE9, 0x96, 0x7A } },
105 { "DarkSeaGreen", { 0x8F, 0xBC, 0x8F } },
106 { "DarkSlateBlue", { 0x48, 0x3D, 0x8B } },
107 { "DarkSlateGray", { 0x2F, 0x4F, 0x4F } },
108 { "DarkTurquoise", { 0x00, 0xCE, 0xD1 } },
109 { "DarkViolet", { 0x94, 0x00, 0xD3 } },
110 { "DeepPink", { 0xFF, 0x14, 0x93 } },
111 { "DeepSkyBlue", { 0x00, 0xBF, 0xFF } },
112 { "DimGray", { 0x69, 0x69, 0x69 } },
113 { "DodgerBlue", { 0x1E, 0x90, 0xFF } },
114 { "FireBrick", { 0xB2, 0x22, 0x22 } },
115 { "FloralWhite", { 0xFF, 0xFA, 0xF0 } },
116 { "ForestGreen", { 0x22, 0x8B, 0x22 } },
117 { "Fuchsia", { 0xFF, 0x00, 0xFF } },
118 { "Gainsboro", { 0xDC, 0xDC, 0xDC } },
119 { "GhostWhite", { 0xF8, 0xF8, 0xFF } },
120 { "Gold", { 0xFF, 0xD7, 0x00 } },
121 { "GoldenRod", { 0xDA, 0xA5, 0x20 } },
122 { "Gray", { 0x80, 0x80, 0x80 } },
123 { "Green", { 0x00, 0x80, 0x00 } },
124 { "GreenYellow", { 0xAD, 0xFF, 0x2F } },
125 { "HoneyDew", { 0xF0, 0xFF, 0xF0 } },
126 { "HotPink", { 0xFF, 0x69, 0xB4 } },
127 { "IndianRed", { 0xCD, 0x5C, 0x5C } },
128 { "Indigo", { 0x4B, 0x00, 0x82 } },
129 { "Ivory", { 0xFF, 0xFF, 0xF0 } },
130 { "Khaki", { 0xF0, 0xE6, 0x8C } },
131 { "Lavender", { 0xE6, 0xE6, 0xFA } },
132 { "LavenderBlush", { 0xFF, 0xF0, 0xF5 } },
133 { "LawnGreen", { 0x7C, 0xFC, 0x00 } },
134 { "LemonChiffon", { 0xFF, 0xFA, 0xCD } },
135 { "LightBlue", { 0xAD, 0xD8, 0xE6 } },
136 { "LightCoral", { 0xF0, 0x80, 0x80 } },
137 { "LightCyan", { 0xE0, 0xFF, 0xFF } },
138 { "LightGoldenRodYellow", { 0xFA, 0xFA, 0xD2 } },
139 { "LightGrey", { 0xD3, 0xD3, 0xD3 } },
140 { "LightGreen", { 0x90, 0xEE, 0x90 } },
141 { "LightPink", { 0xFF, 0xB6, 0xC1 } },
142 { "LightSalmon", { 0xFF, 0xA0, 0x7A } },
143 { "LightSeaGreen", { 0x20, 0xB2, 0xAA } },
144 { "LightSkyBlue", { 0x87, 0xCE, 0xFA } },
145 { "LightSlateGray", { 0x77, 0x88, 0x99 } },
146 { "LightSteelBlue", { 0xB0, 0xC4, 0xDE } },
147 { "LightYellow", { 0xFF, 0xFF, 0xE0 } },
148 { "Lime", { 0x00, 0xFF, 0x00 } },
149 { "LimeGreen", { 0x32, 0xCD, 0x32 } },
150 { "Linen", { 0xFA, 0xF0, 0xE6 } },
151 { "Magenta", { 0xFF, 0x00, 0xFF } },
152 { "Maroon", { 0x80, 0x00, 0x00 } },
153 { "MediumAquaMarine", { 0x66, 0xCD, 0xAA } },
154 { "MediumBlue", { 0x00, 0x00, 0xCD } },
155 { "MediumOrchid", { 0xBA, 0x55, 0xD3 } },
156 { "MediumPurple", { 0x93, 0x70, 0xD8 } },
157 { "MediumSeaGreen", { 0x3C, 0xB3, 0x71 } },
158 { "MediumSlateBlue", { 0x7B, 0x68, 0xEE } },
159 { "MediumSpringGreen", { 0x00, 0xFA, 0x9A } },
160 { "MediumTurquoise", { 0x48, 0xD1, 0xCC } },
161 { "MediumVioletRed", { 0xC7, 0x15, 0x85 } },
162 { "MidnightBlue", { 0x19, 0x19, 0x70 } },
163 { "MintCream", { 0xF5, 0xFF, 0xFA } },
164 { "MistyRose", { 0xFF, 0xE4, 0xE1 } },
165 { "Moccasin", { 0xFF, 0xE4, 0xB5 } },
166 { "NavajoWhite", { 0xFF, 0xDE, 0xAD } },
167 { "Navy", { 0x00, 0x00, 0x80 } },
168 { "OldLace", { 0xFD, 0xF5, 0xE6 } },
169 { "Olive", { 0x80, 0x80, 0x00 } },
170 { "OliveDrab", { 0x6B, 0x8E, 0x23 } },
171 { "Orange", { 0xFF, 0xA5, 0x00 } },
172 { "OrangeRed", { 0xFF, 0x45, 0x00 } },
173 { "Orchid", { 0xDA, 0x70, 0xD6 } },
174 { "PaleGoldenRod", { 0xEE, 0xE8, 0xAA } },
175 { "PaleGreen", { 0x98, 0xFB, 0x98 } },
176 { "PaleTurquoise", { 0xAF, 0xEE, 0xEE } },
177 { "PaleVioletRed", { 0xD8, 0x70, 0x93 } },
178 { "PapayaWhip", { 0xFF, 0xEF, 0xD5 } },
179 { "PeachPuff", { 0xFF, 0xDA, 0xB9 } },
180 { "Peru", { 0xCD, 0x85, 0x3F } },
181 { "Pink", { 0xFF, 0xC0, 0xCB } },
182 { "Plum", { 0xDD, 0xA0, 0xDD } },
183 { "PowderBlue", { 0xB0, 0xE0, 0xE6 } },
184 { "Purple", { 0x80, 0x00, 0x80 } },
185 { "Red", { 0xFF, 0x00, 0x00 } },
186 { "RosyBrown", { 0xBC, 0x8F, 0x8F } },
187 { "RoyalBlue", { 0x41, 0x69, 0xE1 } },
188 { "SaddleBrown", { 0x8B, 0x45, 0x13 } },
189 { "Salmon", { 0xFA, 0x80, 0x72 } },
190 { "SandyBrown", { 0xF4, 0xA4, 0x60 } },
191 { "SeaGreen", { 0x2E, 0x8B, 0x57 } },
192 { "SeaShell", { 0xFF, 0xF5, 0xEE } },
193 { "Sienna", { 0xA0, 0x52, 0x2D } },
194 { "Silver", { 0xC0, 0xC0, 0xC0 } },
195 { "SkyBlue", { 0x87, 0xCE, 0xEB } },
196 { "SlateBlue", { 0x6A, 0x5A, 0xCD } },
197 { "SlateGray", { 0x70, 0x80, 0x90 } },
198 { "Snow", { 0xFF, 0xFA, 0xFA } },
199 { "SpringGreen", { 0x00, 0xFF, 0x7F } },
200 { "SteelBlue", { 0x46, 0x82, 0xB4 } },
201 { "Tan", { 0xD2, 0xB4, 0x8C } },
202 { "Teal", { 0x00, 0x80, 0x80 } },
203 { "Thistle", { 0xD8, 0xBF, 0xD8 } },
204 { "Tomato", { 0xFF, 0x63, 0x47 } },
205 { "Turquoise", { 0x40, 0xE0, 0xD0 } },
206 { "Violet", { 0xEE, 0x82, 0xEE } },
207 { "Wheat", { 0xF5, 0xDE, 0xB3 } },
208 { "White", { 0xFF, 0xFF, 0xFF } },
209 { "WhiteSmoke", { 0xF5, 0xF5, 0xF5 } },
210 { "Yellow", { 0xFF, 0xFF, 0x00 } },
211 { "YellowGreen", { 0x9A, 0xCD, 0x32 } },
212 };
213
214 static int color_table_compare(const void *lhs, const void *rhs)
215 {
216 return strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
217 }
218
219 int av_parse_color(uint8_t *rgba_color, const char *color_string, void *log_ctx)
220 {
221 if (!strcasecmp(color_string, "random") || !strcasecmp(color_string, "bikeshed")) {
222 int rgba = av_get_random_seed();
223 rgba_color[0] = rgba >> 24;
224 rgba_color[1] = rgba >> 16;
225 rgba_color[2] = rgba >> 8;
226 rgba_color[3] = rgba;
227 } else
228 if (!strncmp(color_string, "0x", 2)) {
229 char *tail;
230 int len = strlen(color_string);
231 unsigned int rgba = strtoul(color_string, &tail, 16);
232
233 if (*tail || (len != 8 && len != 10)) {
234 av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string);
235 return AVERROR(EINVAL);
236 }
237 if (len == 10) {
238 rgba_color[3] = rgba;
239 rgba >>= 8;
240 }
241 rgba_color[0] = rgba >> 16;
242 rgba_color[1] = rgba >> 8;
243 rgba_color[2] = rgba;
244 } else {
245 const ColorEntry *entry = bsearch(color_string,
246 color_table,
247 FF_ARRAY_ELEMS(color_table),
248 sizeof(ColorEntry),
249 color_table_compare);
250 if (!entry) {
251 av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string);
252 return AVERROR(EINVAL);
253 }
254 memcpy(rgba_color, entry->rgb_color, 3);
255 }
256
257 return 0;
258 }
259
260 /**
261 * Stores the value in the field in ctx that is named like key.
262 * ctx must be an AVClass context, storing is done using AVOptions.
263 *
264 * @param buf the string to parse, buf will be updated to point at the
265 * separator just after the parsed key/value pair
266 * @param key_val_sep a 0-terminated list of characters used to
267 * separate key from value
268 * @param pairs_sep a 0-terminated list of characters used to separate
269 * two pairs from each other
270 * @return 0 if the key/value pair has been successfully parsed and
271 * set, or a negative value corresponding to an AVERROR code in case
272 * of error:
273 * AVERROR(EINVAL) if the key/value pair cannot be parsed,
274 * the error code issued by av_set_string3() if the key/value pair
275 * cannot be set
276 */
277 static int parse_key_value_pair(void *ctx, const char **buf,
278 const char *key_val_sep, const char *pairs_sep)
279 {
280 char *key = av_get_token(buf, key_val_sep);
281 char *val;
282 int ret;
283
284 if (*key && strspn(*buf, key_val_sep)) {
285 (*buf)++;
286 val = av_get_token(buf, pairs_sep);
287 } else {
288 av_log(ctx, AV_LOG_ERROR, "Missing key or no key/value separator found after key '%s'\n", key);
289 av_free(key);
290 return AVERROR(EINVAL);
291 }
292
293 av_log(ctx, AV_LOG_DEBUG, "Setting value '%s' for key '%s'\n", val, key);
294
295 ret = av_set_string3(ctx, key, val, 1, NULL);
296 if (ret == AVERROR(ENOENT))
297 av_log(ctx, AV_LOG_ERROR, "Key '%s' not found.\n", key);
298
299 av_free(key);
300 av_free(val);
301 return ret;
302 }
303
304 int av_set_options_string(void *ctx, const char *opts,
305 const char *key_val_sep, const char *pairs_sep)
306 {
307 int ret, count = 0;
308
309 while (*opts) {
310 if ((ret = parse_key_value_pair(ctx, &opts, key_val_sep, pairs_sep)) < 0)
311 return ret;
312 count++;
313
314 if (*opts)
315 opts++;
316 }
317
318 return count;
319 }
320
321 #ifdef TEST
322
323 #undef printf
324
325 typedef struct TestContext
326 {
327 const AVClass *class;
328 int num;
329 int toggle;
330 char *string;
331 int flags;
332 AVRational rational;
333 } TestContext;
334
335 #define OFFSET(x) offsetof(TestContext, x)
336
337 #define TEST_FLAG_COOL 01
338 #define TEST_FLAG_LAME 02
339 #define TEST_FLAG_MU 04
340
341 static const AVOption test_options[]= {
342 {"num", "set num", OFFSET(num), FF_OPT_TYPE_INT, 0, 0, 100 },
343 {"toggle", "set toggle", OFFSET(toggle), FF_OPT_TYPE_INT, 0, 0, 1 },
344 {"rational", "set rational", OFFSET(rational), FF_OPT_TYPE_RATIONAL, 0, 0, 10 },
345 {"string", "set string", OFFSET(string), FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX },
346 {"flags", "set flags", OFFSET(flags), FF_OPT_TYPE_FLAGS, 0, 0, INT_MAX, 0, "flags" },
347 {"cool", "set cool flag ", 0, FF_OPT_TYPE_CONST, TEST_FLAG_COOL, INT_MIN, INT_MAX, 0, "flags" },
348 {"lame", "set lame flag ", 0, FF_OPT_TYPE_CONST, TEST_FLAG_LAME, INT_MIN, INT_MAX, 0, "flags" },
349 {"mu", "set mu flag ", 0, FF_OPT_TYPE_CONST, TEST_FLAG_MU, INT_MIN, INT_MAX, 0, "flags" },
350 {NULL},
351 };
352
353 static const char *test_get_name(void *ctx)
354 {
355 return "test";
356 }
357
358 static const AVClass test_class = {
359 "TestContext",
360 test_get_name,
361 test_options
362 };
363
364 int main(void)
365 {
366 int i;
367
368 const char *strings[] = {
369 "''",
370 "",
371 ":",
372 "\\",
373 "'",
374 " '' :",
375 " '' '' :",
376 "foo '' :",
377 "'foo'",
378 "foo ",
379 "foo\\",
380 "foo': blah:blah",
381 "foo\\: blah:blah",
382 "foo\'",
383 "'foo : ' :blahblah",
384 "\\ :blah",
385 " foo",
386 " foo ",
387 " foo \\ ",
388 "foo ':blah",
389 " foo bar : blahblah",
390 "\\f\\o\\o",
391 "'foo : \\ \\ ' : blahblah",
392 "'\\fo\\o:': blahblah",
393 "\\'fo\\o\\:': foo ' :blahblah"
394 };
395
396 for (i=0; i < FF_ARRAY_ELEMS(strings); i++) {
397 const char *p= strings[i];
398 printf("|%s|", p);
399 printf(" -> |%s|", av_get_token(&p, ":"));
400 printf(" + |%s|\n", p);
401 }
402
403 printf("\nTesting av_parse_color()\n");
404 {
405 uint8_t rgba[4];
406 const char *color_names[] = {
407 "bikeshed",
408 "RaNdOm",
409 "foo",
410 "red",
411 "Red ",
412 "RED",
413 "Violet",
414 "Yellow",
415 "Red",
416 "0x000000",
417 "0x0000000",
418 "0xff000000",
419 "0x3e34ff",
420 "0x3e34ffaa",
421 "0xffXXee",
422 "0xfoobar",
423 "0xffffeeeeeeee",
424 };
425
426 av_log_set_level(AV_LOG_DEBUG);
427
428 for (int i = 0; i < FF_ARRAY_ELEMS(color_names); i++) {
429 if (av_parse_color(rgba, color_names[i], NULL) >= 0)
430 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
431 }
432 }
433
434 printf("\nTesting av_set_options_string()\n");
435 {
436 TestContext test_ctx;
437 const char *options[] = {
438 "",
439 ":",
440 "=",
441 "foo=:",
442 ":=foo",
443 "=foo",
444 "foo=",
445 "foo",
446 "foo=val",
447 "foo==val",
448 "toggle=:",
449 "string=:",
450 "toggle=1 : foo",
451 "toggle=100",
452 "toggle==1",
453 "flags=+mu-lame : num=42: toggle=0",
454 "num=42 : string=blahblah",
455 "rational=0 : rational=1/2 : rational=1/-1",
456 "rational=-1/0",
457 };
458
459 test_ctx.class = &test_class;
460 av_opt_set_defaults2(&test_ctx, 0, 0);
461 test_ctx.string = av_strdup("default");
462
463 av_log_set_level(AV_LOG_DEBUG);
464
465 for (i=0; i < FF_ARRAY_ELEMS(options); i++) {
466 av_log(&test_ctx, AV_LOG_DEBUG, "Setting options string '%s'\n", options[i]);
467 if (av_set_options_string(&test_ctx, options[i], "=", ":") < 0)
468 av_log(&test_ctx, AV_LOG_ERROR, "Error setting options string: '%s'\n", options[i]);
469 printf("\n");
470 }
471 }
472
473 return 0;
474 }
475
476 #endif