Coverage Report

Created: 2026-05-16 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/av1-grain-0.2.5/src/parse.rs
Line
Count
Source
1
// Copyright (c) 2022-2022, The rav1e contributors. All rights reserved
2
//
3
// This source code is subject to the terms of the BSD 2 Clause License and
4
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5
// was not distributed with this source code in the LICENSE file, you can
6
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7
// Media Patent License 1.0 was not distributed with this source code in the
8
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10
use arrayvec::ArrayVec;
11
use nom::{
12
    branch::alt,
13
    bytes::complete::tag,
14
    character::complete::{char, digit1, line_ending, multispace0, multispace1, space0, space1},
15
    combinator::{eof, map_res, opt, recognize},
16
    error::{Error as NomError, ErrorKind, FromExternalError, ParseError},
17
    multi::{many1, separated_list0, separated_list1},
18
    sequence::{delimited, preceded},
19
    AsChar, Compare, Err as NomErr, IResult, Input, Parser,
20
};
21
22
use crate::{GrainTableSegment, NUM_UV_COEFFS, NUM_UV_POINTS, NUM_Y_COEFFS, NUM_Y_POINTS};
23
24
/// This file has the implementation details of the grain table.
25
///
26
/// The file format is an ascii representation for readability and
27
/// editability. Array parameters are separated from the non-array
28
/// parameters and prefixed with a few characters to make for easy
29
/// localization with a parameter set. Each entry is prefixed with "E"
30
/// and the other parameters are only specified if "apply-grain" is
31
/// non-zero.
32
///
33
/// ```text
34
/// filmgrn1
35
/// E <start-time> <end-time> <apply-grain> <random-seed> <dynamic-grain>
36
///  p <ar_coeff_lag> <ar_coeff_shift> <grain_scale_shift> ...
37
///  sY <num_y_points> <point_0_x> <point_0_y> ...
38
///  sCb <num_cb_points> <point_0_x> <point_0_y> ...
39
///  sCr <num_cr_points> <point_0_x> <point_0_y> ...
40
///  cY <ar_coeff_y_0> ....
41
///  cCb <ar_coeff_cb_0> ....
42
///  cCr <ar_coeff_cr_0> ....
43
/// E <start-time> ...
44
/// ```
45
///
46
/// # Errors
47
///
48
/// - If the file cannot be opened
49
/// - If the file does not contain a properly formatted film grain table
50
#[inline]
51
0
pub fn parse_grain_table(input: &str) -> anyhow::Result<Vec<GrainTableSegment>> {
52
0
    let (input, _) = grain_table_header(input).map_err(|e| anyhow::anyhow!(e.to_string()))?;
53
0
    let (_, segments) = many1(grain_table_segment)
54
0
        .parse(input)
55
0
        .map_err(|e| anyhow::anyhow!(e.to_string()))?;
56
0
    Ok(segments.into_iter().flatten().collect())
57
0
}
58
59
0
fn grain_table_header(input: &str) -> IResult<&str, ()> {
60
0
    let (input, _) = delimited(multispace0, tag("filmgrn1"), line_ending).parse(input)?;
61
0
    Ok((input, ()))
62
0
}
63
64
0
fn line<I, O, E: ParseError<I>, F>(parser: F) -> impl Parser<I, Output = O, Error = E>
65
0
where
66
0
    I: Input + Clone + Compare<&'static str>,
67
0
    <I as Input>::Item: AsChar + Clone,
68
0
    F: Parser<I, Output = O, Error = E>,
69
{
70
0
    delimited(multispace0, parser, alt((line_ending, eof)))
71
0
}
Unexecuted instantiation: av1_grain::parse::line::<&str, alloc::vec::Vec<&str>, nom::error::Error<&str>, nom::sequence::Preceded<nom::bytes::complete::tag<&str, &str, nom::error::Error<&str>>::{closure#0}, nom::sequence::Preceded<nom::character::complete::space0<&str, nom::error::Error<&str>>, nom::multi::SeparatedList0<av1_grain::parse::integer, nom::character::complete::multispace1<&str, nom::error::Error<&str>>>>>>
Unexecuted instantiation: av1_grain::parse::line::<&str, alloc::vec::Vec<&str>, nom::error::Error<&str>, nom::sequence::Preceded<nom::bytes::complete::tag<&str, &str, nom::error::Error<&str>>::{closure#0}, nom::sequence::Preceded<nom::character::complete::space1<&str, nom::error::Error<&str>>, nom::multi::SeparatedList1<nom::character::complete::digit1<&str, nom::error::Error<&str>>, nom::character::complete::space1<&str, nom::error::Error<&str>>>>>>
Unexecuted instantiation: av1_grain::parse::line::<&str, alloc::vec::Vec<&str>, nom::error::Error<&str>, nom::sequence::Preceded<nom::bytes::complete::tag<&str, &str, nom::error::Error<&str>>::{closure#0}, nom::sequence::Preceded<nom::character::complete::space1<&str, nom::error::Error<&str>>, nom::multi::SeparatedList1<av1_grain::parse::integer, nom::character::complete::multispace1<&str, nom::error::Error<&str>>>>>>
72
73
0
fn grain_table_segment(input: &str) -> IResult<&str, Option<GrainTableSegment>> {
74
0
    let (input, e_params) = e_params(input)?;
75
0
    if !e_params.apply {
76
        // I'm not sure *why* there's even an option to generate a film grain config
77
        // that doesn't apply film grain. But, well, I didn't make this format.
78
0
        return Ok((input, None));
79
0
    }
80
81
0
    let (input, p_params) = p_params(input)?;
82
0
    let (input, s_y_params) = s_y_params(input)?;
83
0
    let (input, s_cb_params) = s_cb_params(input)?;
84
0
    let (input, s_cr_params) = s_cr_params(input)?;
85
0
    let coeff_count = (2 * p_params.ar_coeff_lag * (p_params.ar_coeff_lag + 1)) as usize;
86
0
    let (input, c_y_params) = c_y_params(input, coeff_count)?;
87
0
    let (input, c_cb_params) = c_cb_params(input, coeff_count)?;
88
0
    let (input, c_cr_params) = c_cr_params(input, coeff_count)?;
89
90
0
    Ok((
91
0
        input,
92
0
        Some(GrainTableSegment {
93
0
            start_time: e_params.start,
94
0
            end_time: e_params.end,
95
0
            scaling_points_y: s_y_params,
96
0
            scaling_points_cb: s_cb_params,
97
0
            scaling_points_cr: s_cr_params,
98
0
            scaling_shift: p_params.scaling_shift,
99
0
            ar_coeff_lag: p_params.ar_coeff_lag,
100
0
            ar_coeffs_y: c_y_params,
101
0
            ar_coeffs_cb: c_cb_params,
102
0
            ar_coeffs_cr: c_cr_params,
103
0
            ar_coeff_shift: p_params.ar_coeff_shift,
104
0
            cb_mult: p_params.cb_mult,
105
0
            cb_luma_mult: p_params.cb_luma_mult,
106
0
            cb_offset: p_params.cb_offset,
107
0
            cr_mult: p_params.cr_mult,
108
0
            cr_luma_mult: p_params.cr_luma_mult,
109
0
            cr_offset: p_params.cr_offset,
110
0
            overlap_flag: p_params.overlap_flag,
111
0
            chroma_scaling_from_luma: p_params.chroma_scaling_from_luma,
112
0
            grain_scale_shift: p_params.grain_scale_shift,
113
0
            random_seed: e_params.seed,
114
0
        }),
115
0
    ))
116
0
}
117
118
#[derive(Debug, Clone, Copy)]
119
struct EParams {
120
    pub start: u64,
121
    pub end: u64,
122
    pub apply: bool,
123
    pub seed: u16,
124
}
125
126
0
fn e_params(input: &str) -> IResult<&str, EParams> {
127
0
    let (input, params) = map_res(
128
0
        line(preceded(
129
0
            tag("E"),
130
0
            preceded(space1, separated_list1(space1, digit1)),
131
        )),
132
0
        |items: Vec<&str>| {
133
0
            if items.len() != 5 {
134
0
                return Err(NomErr::<NomError<&str>>::Failure(
135
0
                    NomError::from_external_error(
136
0
                        input,
137
0
                        ErrorKind::Verify,
138
0
                        "Expected 5 values on E line",
139
0
                    ),
140
0
                ));
141
0
            }
142
0
            let parsed = EParams {
143
0
                start: items[0].parse().map_err(|_e| {
144
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
145
0
                        input,
146
0
                        ErrorKind::Digit,
147
0
                        "Failed to parse start_time",
148
0
                    ))
149
0
                })?,
150
0
                end: items[1].parse().map_err(|_e| {
151
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
152
0
                        input,
153
0
                        ErrorKind::Digit,
154
0
                        "Failed to parse end_time",
155
0
                    ))
156
0
                })?,
157
0
                apply: items[2].parse::<u8>().map_err(|_e| {
158
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
159
0
                        input,
160
0
                        ErrorKind::Digit,
161
0
                        "Failed to parse apply_grain",
162
0
                    ))
163
0
                })? > 0,
164
0
                seed: items[3].parse().map_err(|_e| {
165
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
166
0
                        input,
167
0
                        ErrorKind::Digit,
168
0
                        "Failed to parse random_seed",
169
0
                    ))
170
0
                })?,
171
            };
172
0
            Ok::<_, NomErr<NomError<&str>>>(parsed)
173
0
        },
174
    )
175
0
    .parse(input)?;
176
177
0
    if params.end < params.start {
178
0
        return Err(NomErr::Failure(NomError::from_external_error(
179
0
            input,
180
0
            ErrorKind::Verify,
181
0
            "Start time must be before end time",
182
0
        )));
183
0
    }
184
185
0
    Ok((input, params))
186
0
}
187
188
#[derive(Debug, Clone, Copy)]
189
struct PParams {
190
    ar_coeff_lag: u8,
191
    ar_coeff_shift: u8,
192
    grain_scale_shift: u8,
193
    scaling_shift: u8,
194
    chroma_scaling_from_luma: bool,
195
    overlap_flag: bool,
196
    cb_mult: u8,
197
    cb_luma_mult: u8,
198
    cb_offset: u16,
199
    cr_mult: u8,
200
    cr_luma_mult: u8,
201
    cr_offset: u16,
202
}
203
204
#[allow(clippy::too_many_lines)]
205
0
fn p_params(input: &str) -> IResult<&str, PParams> {
206
0
    let (input, params) = map_res(
207
0
        line(preceded(
208
0
            tag("p"),
209
0
            preceded(space1, separated_list1(space1, digit1)),
210
        )),
211
0
        |items: Vec<&str>| {
212
0
            if items.len() != 12 {
213
0
                return Err(NomErr::<NomError<&str>>::Failure(
214
0
                    NomError::from_external_error(
215
0
                        input,
216
0
                        ErrorKind::Verify,
217
0
                        "Expected 12 values on p line",
218
0
                    ),
219
0
                ));
220
0
            }
221
222
0
            let parsed = PParams {
223
0
                ar_coeff_lag: items[0].parse().map_err(|_e| {
224
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
225
0
                        input,
226
0
                        ErrorKind::Digit,
227
0
                        "Failed to parse ar_coeff_lag",
228
0
                    ))
229
0
                })?,
230
0
                ar_coeff_shift: items[1].parse().map_err(|_e| {
231
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
232
0
                        input,
233
0
                        ErrorKind::Digit,
234
0
                        "Failed to parse ar_coeff_shift",
235
0
                    ))
236
0
                })?,
237
0
                grain_scale_shift: items[2].parse().map_err(|_e| {
238
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
239
0
                        input,
240
0
                        ErrorKind::Digit,
241
0
                        "Failed to parse grain_scale_shift",
242
0
                    ))
243
0
                })?,
244
0
                scaling_shift: items[3].parse().map_err(|_e| {
245
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
246
0
                        input,
247
0
                        ErrorKind::Digit,
248
0
                        "Failed to parse scaling_shift",
249
0
                    ))
250
0
                })?,
251
0
                chroma_scaling_from_luma: items[4].parse::<u8>().map_err(|_e| {
252
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
253
0
                        input,
254
0
                        ErrorKind::Digit,
255
0
                        "Failed to parse chroma_scaling_from_luma",
256
0
                    ))
257
0
                })? > 0,
258
0
                overlap_flag: items[5].parse::<u8>().map_err(|_e| {
259
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
260
0
                        input,
261
0
                        ErrorKind::Digit,
262
0
                        "Failed to parse overlap_flag",
263
0
                    ))
264
0
                })? > 0,
265
0
                cb_mult: items[6].parse().map_err(|_e| {
266
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
267
0
                        input,
268
0
                        ErrorKind::Digit,
269
0
                        "Failed to parse cb_mult",
270
0
                    ))
271
0
                })?,
272
0
                cb_luma_mult: items[7].parse().map_err(|_e| {
273
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
274
0
                        input,
275
0
                        ErrorKind::Digit,
276
0
                        "Failed to parse cb_luma_mult",
277
0
                    ))
278
0
                })?,
279
0
                cb_offset: items[8].parse().map_err(|_e| {
280
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
281
0
                        input,
282
0
                        ErrorKind::Digit,
283
0
                        "Failed to parse cb_offset",
284
0
                    ))
285
0
                })?,
286
0
                cr_mult: items[9].parse().map_err(|_e| {
287
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
288
0
                        input,
289
0
                        ErrorKind::Digit,
290
0
                        "Failed to parse cr_mult",
291
0
                    ))
292
0
                })?,
293
0
                cr_luma_mult: items[10].parse().map_err(|_e| {
294
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
295
0
                        input,
296
0
                        ErrorKind::Digit,
297
0
                        "Failed to parse cr_luma_mult",
298
0
                    ))
299
0
                })?,
300
0
                cr_offset: items[11].parse().map_err(|_e| {
301
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
302
0
                        input,
303
0
                        ErrorKind::Digit,
304
0
                        "Failed to parse cr_offset",
305
0
                    ))
306
0
                })?,
307
            };
308
0
            Ok::<_, NomErr<NomError<&str>>>(parsed)
309
0
        },
310
    )
311
0
    .parse(input)?;
312
313
0
    if params.scaling_shift < 8 || params.scaling_shift > 11 {
314
0
        return Err(NomErr::Failure(NomError::from_external_error(
315
0
            input,
316
0
            ErrorKind::Verify,
317
0
            "scaling_shift must be between 8 and 11",
318
0
        )));
319
0
    }
320
0
    if params.ar_coeff_lag > 3 {
321
0
        return Err(NomErr::Failure(NomError::from_external_error(
322
0
            input,
323
0
            ErrorKind::Verify,
324
0
            "ar_coeff_lag must be between 0 and 3",
325
0
        )));
326
0
    }
327
0
    if params.ar_coeff_shift < 6 || params.ar_coeff_shift > 9 {
328
0
        return Err(NomErr::Failure(NomError::from_external_error(
329
0
            input,
330
0
            ErrorKind::Verify,
331
0
            "ar_coeff_shift must be between 6 and 9",
332
0
        )));
333
0
    }
334
335
0
    Ok((input, params))
336
0
}
337
338
0
fn s_y_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_Y_POINTS>> {
339
0
    let (input, values) = map_res(
340
0
        line(preceded(
341
0
            tag("sY"),
342
0
            preceded(space1, separated_list1(space1, digit1)),
343
        )),
344
0
        |items: Vec<&str>| {
345
0
            let mut parsed = Vec::with_capacity(items.len());
346
0
            for item in items {
347
0
                parsed.push(item.parse::<u8>().map_err(|_e| {
348
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
349
0
                        input,
350
0
                        ErrorKind::Digit,
351
0
                        "Failed to parse Y-plane points",
352
0
                    ))
353
0
                })?);
354
            }
355
0
            Ok::<_, NomErr<NomError<&str>>>(parsed)
356
0
        },
357
    )
358
0
    .parse(input)?;
359
360
0
    let len = values[0] as usize;
361
0
    if values.len() != len * 2 + 1 {
362
0
        return Err(NomErr::Failure(NomError::from_external_error(
363
0
            input,
364
0
            ErrorKind::Verify,
365
0
            format!(
366
0
                "Expected {} Y-plane points, got {}",
367
0
                len * 2,
368
0
                values.len() - 1
369
0
            ),
370
0
        )));
371
0
    }
372
373
    Ok((
374
0
        input,
375
0
        values[1..]
376
0
            .chunks_exact(2)
377
0
            .map(|chunk| [chunk[0], chunk[1]])
378
0
            .collect(),
379
    ))
380
0
}
381
382
0
fn s_cb_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_UV_POINTS>> {
383
0
    let (input, values) = map_res(
384
0
        line(preceded(
385
0
            tag("sCb"),
386
0
            preceded(space1, separated_list1(space1, digit1)),
387
        )),
388
0
        |items: Vec<&str>| {
389
0
            let mut parsed = Vec::with_capacity(items.len());
390
0
            for item in items {
391
0
                parsed.push(item.parse::<u8>().map_err(|_e| {
392
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
393
0
                        input,
394
0
                        ErrorKind::Digit,
395
0
                        "Failed to parse Cb-plane points",
396
0
                    ))
397
0
                })?);
398
            }
399
0
            Ok::<_, NomErr<NomError<&str>>>(parsed)
400
0
        },
401
    )
402
0
    .parse(input)?;
403
404
0
    let len = values[0] as usize;
405
0
    if values.len() != len * 2 + 1 {
406
0
        return Err(NomErr::Failure(NomError::from_external_error(
407
0
            input,
408
0
            ErrorKind::Verify,
409
0
            format!(
410
0
                "Expected {} Cb-plane points, got {}",
411
0
                len * 2,
412
0
                values.len() - 1
413
0
            ),
414
0
        )));
415
0
    }
416
417
    Ok((
418
0
        input,
419
0
        values[1..]
420
0
            .chunks_exact(2)
421
0
            .map(|chunk| [chunk[0], chunk[1]])
422
0
            .collect(),
423
    ))
424
0
}
425
426
0
fn s_cr_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_UV_POINTS>> {
427
0
    let (input, values) = map_res(
428
0
        line(preceded(
429
0
            tag("sCr"),
430
0
            preceded(space1, separated_list1(space1, digit1)),
431
        )),
432
0
        |items: Vec<&str>| {
433
0
            let mut parsed = Vec::with_capacity(items.len());
434
0
            for item in items {
435
0
                parsed.push(item.parse::<u8>().map_err(|_e| {
436
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
437
0
                        input,
438
0
                        ErrorKind::Digit,
439
0
                        "Failed to parse Cr-plane points",
440
0
                    ))
441
0
                })?);
442
            }
443
0
            Ok::<_, NomErr<NomError<&str>>>(parsed)
444
0
        },
445
    )
446
0
    .parse(input)?;
447
448
0
    let len = values[0] as usize;
449
0
    if values.len() != len * 2 + 1 {
450
0
        return Err(NomErr::Failure(NomError::from_external_error(
451
0
            input,
452
0
            ErrorKind::Verify,
453
0
            format!(
454
0
                "Expected {} Cr-plane points, got {}",
455
0
                len * 2,
456
0
                values.len() - 1
457
0
            ),
458
0
        )));
459
0
    }
460
461
    Ok((
462
0
        input,
463
0
        values[1..]
464
0
            .chunks_exact(2)
465
0
            .map(|chunk| [chunk[0], chunk[1]])
466
0
            .collect(),
467
    ))
468
0
}
469
470
0
fn integer(input: &str) -> IResult<&str, &str> {
471
0
    recognize(preceded(opt(char('-')), digit1)).parse(input)
472
0
}
473
474
0
fn c_y_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_Y_COEFFS>> {
475
0
    let (input, values) = map_res(
476
0
        line(preceded(
477
0
            tag("cY"),
478
0
            preceded(space0, separated_list0(multispace1, integer)),
479
        )),
480
0
        |items: Vec<&str>| {
481
0
            let mut parsed = Vec::with_capacity(items.len());
482
0
            for item in items {
483
0
                parsed.push(item.parse::<i8>().map_err(|_e| {
484
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
485
0
                        input,
486
0
                        ErrorKind::Digit,
487
0
                        "Failed to parse Y-plane coeffs",
488
0
                    ))
489
0
                })?);
490
            }
491
0
            Ok::<_, NomErr<NomError<&str>>>(parsed)
492
0
        },
493
    )
494
0
    .parse(input)?;
495
496
0
    if values.len() != count {
497
0
        return Err(NomErr::Failure(NomError::from_external_error(
498
0
            input,
499
0
            ErrorKind::Verify,
500
0
            format!("Expected {} Y-plane coeffs, got {}", count, values.len()),
501
0
        )));
502
0
    }
503
504
0
    Ok((input, values.into_iter().collect()))
505
0
}
506
507
0
fn c_cb_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_UV_COEFFS>> {
508
0
    let (input, values) = map_res(
509
0
        line(preceded(
510
0
            tag("cCb"),
511
0
            preceded(space1, separated_list1(multispace1, integer)),
512
        )),
513
0
        |items: Vec<&str>| {
514
0
            let mut parsed = Vec::with_capacity(items.len());
515
0
            for item in items {
516
0
                parsed.push(item.parse::<i8>().map_err(|_e| {
517
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
518
0
                        input,
519
0
                        ErrorKind::Digit,
520
0
                        "Failed to parse Cb-plane coeffs",
521
0
                    ))
522
0
                })?);
523
            }
524
0
            Ok::<_, NomErr<NomError<&str>>>(parsed)
525
0
        },
526
    )
527
0
    .parse(input)?;
528
529
0
    if values.len() != count + 1 {
530
0
        return Err(NomErr::Failure(NomError::from_external_error(
531
0
            input,
532
0
            ErrorKind::Verify,
533
0
            format!(
534
0
                "Expected {} Cb-plane coeffs, got {}",
535
0
                count + 1,
536
0
                values.len()
537
0
            ),
538
0
        )));
539
0
    }
540
541
0
    Ok((input, values.into_iter().collect()))
542
0
}
543
544
0
fn c_cr_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_UV_COEFFS>> {
545
0
    let (input, values) = map_res(
546
0
        line(preceded(
547
0
            tag("cCr"),
548
0
            preceded(space1, separated_list1(multispace1, integer)),
549
        )),
550
0
        |items: Vec<&str>| {
551
0
            let mut parsed = Vec::with_capacity(items.len());
552
0
            for item in items {
553
0
                parsed.push(item.parse::<i8>().map_err(|_e| {
554
0
                    NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
555
0
                        input,
556
0
                        ErrorKind::Digit,
557
0
                        "Failed to parse Cr-plane coeffs",
558
0
                    ))
559
0
                })?);
560
            }
561
0
            Ok::<_, NomErr<NomError<&str>>>(parsed)
562
0
        },
563
    )
564
0
    .parse(input)?;
565
566
0
    if values.len() != count + 1 {
567
0
        return Err(NomErr::Failure(NomError::from_external_error(
568
0
            input,
569
0
            ErrorKind::Verify,
570
0
            format!(
571
0
                "Expected {} Cr-plane coeffs, got {}",
572
0
                count + 1,
573
0
                values.len()
574
0
            ),
575
0
        )));
576
0
    }
577
578
0
    Ok((input, values.into_iter().collect()))
579
0
}
580
581
#[cfg(test)]
582
mod tests {
583
    use super::*;
584
585
    #[test]
586
    fn parse_luma_only_table() {
587
        // This is the luma-only table format generated by
588
        // both aomenc's photon noise utility and by av1an.
589
        let input = r#"filmgrn1
590
E 0 9223372036854775807 1 7391 1
591
  p 0 6 0 8 0 1 0 0 0 0 0 0
592
  sY 14  0 20 20 5 39 4 59 3 78 3 98 3 118 3 137 3 157 3 177 3 196 3 216 4 235 4 255 4
593
  sCb 0
594
  sCr 0
595
  cY
596
  cCb 0
597
  cCr 0
598
"#;
599
        let expected = GrainTableSegment {
600
            start_time: 0,
601
            end_time: 9_223_372_036_854_775_807,
602
            scaling_points_y: ArrayVec::from([
603
                [0, 20],
604
                [20, 5],
605
                [39, 4],
606
                [59, 3],
607
                [78, 3],
608
                [98, 3],
609
                [118, 3],
610
                [137, 3],
611
                [157, 3],
612
                [177, 3],
613
                [196, 3],
614
                [216, 4],
615
                [235, 4],
616
                [255, 4],
617
            ]),
618
            scaling_points_cb: ArrayVec::new(),
619
            scaling_points_cr: ArrayVec::new(),
620
            scaling_shift: 8,
621
            ar_coeff_lag: 0,
622
            ar_coeffs_y: ArrayVec::new(),
623
            ar_coeffs_cb: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity"),
624
            ar_coeffs_cr: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity"),
625
            ar_coeff_shift: 6,
626
            cb_mult: 0,
627
            cb_luma_mult: 0,
628
            cb_offset: 0,
629
            cr_mult: 0,
630
            cr_luma_mult: 0,
631
            cr_offset: 0,
632
            overlap_flag: true,
633
            chroma_scaling_from_luma: false,
634
            grain_scale_shift: 0,
635
            random_seed: 7391,
636
        };
637
        let output = parse_grain_table(input).expect("Test failed");
638
        assert_eq!(vec![expected], output);
639
    }
640
641
    #[test]
642
    fn parse_luma_chroma_table() {
643
        // This is the luma+chroma table format generated by
644
        // both aomenc's photon noise utility and by av1an.
645
        let input = r#"filmgrn1
646
E 0 9223372036854775807 1 7391 1
647
  p 0 6 0 8 0 1 128 192 256 128 192 256
648
  sY 14  0 0 20 4 39 3 59 3 78 3 98 3 118 4 137 4 157 4 177 4 196 4 216 5 235 5 255 5
649
  sCb 10 0 0 28 0 57 0 85 0 113 0 142 0 170 0 198 0 227 0 255 1
650
  sCr 10 0 0 28 0 57 0 85 0 113 0 142 0 170 0 198 0 227 0 255 1
651
  cY
652
  cCb 0
653
  cCr 0
654
"#;
655
        let expected = GrainTableSegment {
656
            start_time: 0,
657
            end_time: 9_223_372_036_854_775_807,
658
            scaling_points_y: ArrayVec::from([
659
                [0, 0],
660
                [20, 4],
661
                [39, 3],
662
                [59, 3],
663
                [78, 3],
664
                [98, 3],
665
                [118, 4],
666
                [137, 4],
667
                [157, 4],
668
                [177, 4],
669
                [196, 4],
670
                [216, 5],
671
                [235, 5],
672
                [255, 5],
673
            ]),
674
            scaling_points_cb: ArrayVec::from([
675
                [0, 0],
676
                [28, 0],
677
                [57, 0],
678
                [85, 0],
679
                [113, 0],
680
                [142, 0],
681
                [170, 0],
682
                [198, 0],
683
                [227, 0],
684
                [255, 1],
685
            ]),
686
            scaling_points_cr: ArrayVec::from([
687
                [0, 0],
688
                [28, 0],
689
                [57, 0],
690
                [85, 0],
691
                [113, 0],
692
                [142, 0],
693
                [170, 0],
694
                [198, 0],
695
                [227, 0],
696
                [255, 1],
697
            ]),
698
            scaling_shift: 8,
699
            ar_coeff_lag: 0,
700
            ar_coeffs_y: ArrayVec::new(),
701
            ar_coeffs_cb: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity"),
702
            ar_coeffs_cr: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity"),
703
            ar_coeff_shift: 6,
704
            cb_mult: 128,
705
            cb_luma_mult: 192,
706
            cb_offset: 256,
707
            cr_mult: 128,
708
            cr_luma_mult: 192,
709
            cr_offset: 256,
710
            overlap_flag: true,
711
            chroma_scaling_from_luma: false,
712
            grain_scale_shift: 0,
713
            random_seed: 7391,
714
        };
715
        let output = parse_grain_table(input).expect("Test failed");
716
        assert_eq!(vec![expected], output);
717
    }
718
719
    #[test]
720
    fn parse_complex_table() {
721
        let input = r#"filmgrn1
722
E 0 417083 1 7391 1
723
  p 3 7 0 11 0 1 128 192 256 128 192 256
724
  sY 6  0 53 13 53 40 64 94 49 121 46 255 46
725
  sCb 2 0 14 255 13
726
  sCr 2 0 12 255 14
727
  cY 1 -4 1 4 8 3 -2 -6 9 14 -27 -25 -2 4 5 15 -80 94 28 -3 -2 6 -47 121
728
  cCb -3 1 -4 6 -1 2 -2 1 11 -10 -2 -16 -1 3 -2 -14 -26 65 19 -3 -5 2 -6 75 -1
729
  cCr 0 0 -4 8 -1 0 1 2 -1 -9 4 -7 -5 -2 -5 -14 0 45 18 3 -3 4 8 49 5
730
E 417083 7090416 1 0 1
731
  p 3 7 0 11 0 1 128 192 256 128 192 256
732
  sY 4  0 46 40 54 108 39 255 38
733
  sCb 2 0 14 255 14
734
  sCr 2 0 12 255 14
735
  cY 1 -4 1 5 8 4 -2 -6 9 13 -28 -28 -5 5 5 13 -76 91 32 -1 -3 7 -50 124
736
  cCb -2 1 -3 3 -2 1 -1 2 8 -10 0 -12 -2 2 -1 -14 -20 61 18 -1 -4 -2 -1 70 -1
737
  cCr 0 0 -3 6 -1 -1 0 1 -2 -8 6 -4 -5 -2 -6 -12 4 41 17 4 -2 3 13 44 5
738
E 7090416 7507500 1 0 1
739
  p 3 7 0 11 0 1 128 192 256 128 192 256
740
  sY 4  0 54 40 64 108 46 255 44
741
  sCb 2 0 14 255 13
742
  sCr 2 0 12 255 14
743
  cY 1 -4 2 3 7 3 -2 -6 9 14 -26 -25 -3 5 6 15 -81 95 27 -3 -3 5 -46 121
744
  cCb -2 1 -4 4 -2 1 -1 2 9 -12 3 -13 -1 2 -2 -16 -26 66 17 -2 -5 -1 1 73 0
745
  cCr 1 -1 -5 8 -1 -1 1 1 -3 -9 9 -5 -6 -2 -7 -14 1 44 17 3 -3 5 15 46 4
746
E 7507500 10010000 1 0 1
747
  p 3 7 0 11 0 1 128 192 256 128 192 256
748
  sY 4  0 49 40 59 108 43 255 41
749
  sCb 2 0 14 255 14
750
  sCr 2 0 13 255 15
751
  cY 1 -4 0 6 8 3 -2 -5 8 14 -29 -26 -3 4 3 15 -76 92 29 -2 -3 8 -49 121
752
  cCb -3 0 -3 6 0 1 -2 1 10 -9 -4 -15 -1 2 -1 -13 -22 62 20 -3 -4 2 -7 73 -1
753
  cCr -1 0 -3 6 0 0 0 2 0 -9 2 -7 -5 -1 -4 -14 0 45 19 2 -2 3 7 50 4
754
E 10010000 13346666 1 0 1
755
  p 3 7 0 11 0 1 128 192 256 128 192 256
756
  sY 6  0 33 27 39 40 53 54 55 108 52 255 52
757
  sCb 2 0 16 255 14
758
  sCr 2 0 11 255 12
759
  cY 1 -4 1 5 9 4 -2 -7 12 11 -27 -30 -5 5 6 10 -73 89 35 -1 -3 6 -49 124
760
  cCb -2 0 -2 1 -2 1 -2 0 9 -9 -2 -14 -1 2 0 -11 -26 65 18 -2 -4 -2 -8 75 -5
761
  cCr 0 0 -4 5 -2 0 1 3 -1 -9 6 -5 -5 -1 -6 -14 1 43 18 4 -3 3 13 49 3
762
E 13346666 16683333 1 0 1
763
  p 3 7 0 11 0 1 128 192 256 128 192 256
764
  sY 6  0 36 27 42 40 58 54 60 108 57 255 57
765
  sCb 2 0 15 255 14
766
  sCr 4 0 11 40 17 94 13 255 13
767
  cY 1 -4 1 5 8 3 -2 -6 10 12 -27 -27 -4 4 5 12 -73 90 32 -2 -3 6 -47 121
768
  cCb -2 0 -3 4 -1 1 -2 0 10 -9 -2 -14 1 3 -1 -10 -24 62 16 -2 -4 0 -6 72 -7
769
  cCr 0 0 -3 6 -1 0 1 3 1 -9 3 -7 -5 -1 -5 -14 -2 46 19 2 -3 3 7 54 3
770
E 16683333 17100416 1 0 1
771
  p 3 7 0 11 0 1 128 192 256 128 192 256
772
  sY 7  0 41 13 41 27 49 40 66 54 68 108 65 255 65
773
  sCb 2 0 18 255 14
774
  sCr 4 0 11 40 18 67 14 255 13
775
  cY 0 -3 1 4 7 3 -2 -5 7 13 -27 -23 -3 4 5 15 -79 94 26 -3 -2 5 -45 120
776
  cCb -1 -2 -1 1 0 0 -3 -2 12 -6 -3 -15 3 2 2 -8 -42 75 12 -3 -4 -2 -8 82 -3
777
  cCr 0 0 -5 7 -2 0 1 3 0 -11 6 -7 -5 -1 -6 -15 -5 48 18 2 -3 3 10 55 2
778
E 17100416 20020000 1 0 1
779
  p 3 7 0 11 0 1 128 192 256 128 192 256
780
  sY 6  0 37 27 44 40 61 54 63 108 60 255 60
781
  sCb 2 0 14 255 14
782
  sCr 4 0 11 40 18 94 13 255 13
783
  cY 1 -3 0 6 7 2 -1 -5 7 13 -28 -25 -2 3 3 13 -73 91 29 -2 -2 7 -47 119
784
  cCb -2 -1 -3 4 0 1 -2 -1 11 -7 -6 -15 1 2 -1 -9 -25 63 16 -3 -4 2 -11 73 -8
785
  cCr -1 1 -2 6 0 1 0 2 3 -9 -2 -10 -4 0 -3 -14 -6 50 20 0 -3 3 -1 59 3
786
E 20020000 9223372036854775807 1 0 1
787
  p 3 6 0 11 0 1 128 192 256 128 192 256
788
  sY 6  0 32 27 37 40 50 54 52 121 49 255 49
789
  sCb 4 0 21 40 23 81 17 255 15
790
  sCr 2 0 11 255 12
791
  cY 1 -3 1 2 5 3 -2 -6 8 6 -12 -18 -2 3 5 7 -42 44 21 -3 -1 4 -29 67
792
  cCb -1 0 1 0 -1 0 -1 0 5 -4 -3 -9 1 1 2 -4 -21 39 10 -2 -3 -2 -7 44 1
793
  cCr 1 0 -3 2 -3 -1 0 1 -1 -4 5 -2 -1 -1 -5 -6 3 20 10 4 -2 0 9 23 -1"#;
794
        let output = parse_grain_table(input);
795
        assert!(output.is_ok());
796
    }
797
}