Coverage Report

Created: 2025-07-11 07:25

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