pixelflow-filters 0.1.0

Official in-repository filters for PixelFlow.
use pixelflow_core::{Frame, MetadataSchema, MetadataValue};

pub(crate) use pixelflow_test_support::{
    assert_plane_f32_near, assert_plane_u8_near, assert_plane_u16_near, fixed_media,
    synthetic_f32_frame, synthetic_u8_frame, synthetic_u16_frame,
};

pub(crate) fn with_string_metadata(
    frame: &Frame,
    key: &str,
    value: &str,
) -> pixelflow_core::Result<Frame> {
    let schema = MetadataSchema::core();
    let mut metadata = frame.metadata().clone();
    metadata.set(&schema, key, MetadataValue::String(value.to_owned()))?;
    Ok(frame.with_metadata(metadata))
}

pub(crate) fn with_frame_number_metadata(
    frame: &Frame,
    frame_number: usize,
) -> pixelflow_core::Result<Frame> {
    let schema = MetadataSchema::core();
    let mut metadata = frame.metadata().clone();
    metadata.set(
        &schema,
        "core:frame_number",
        MetadataValue::Int(i64::try_from(frame_number).expect("frame number fits i64")),
    )?;
    Ok(frame.with_metadata(metadata))
}

pub(crate) fn with_none_metadata(frame: &Frame, key: &str) -> pixelflow_core::Result<Frame> {
    let schema = MetadataSchema::core();
    let mut metadata = frame.metadata().clone();
    metadata.set(&schema, key, MetadataValue::None)?;
    Ok(frame.with_metadata(metadata))
}

#[cfg(test)]
mod tests {
    use crate::{EXACT_GOLDEN_TOLERANCE, GoldenTolerance};

    use super::{fixed_media, synthetic_u8_frame};

    #[test]
    fn fixed_media_uses_standard_filter_test_timing() {
        let media = fixed_media("gray8", 8, 6);

        assert!(matches!(
            media.format(),
            pixelflow_core::ClipFormat::Fixed(format) if format.name() == "gray8"
        ));
        assert_eq!(media.frame_count(), pixelflow_core::FrameCount::Finite(24));
        assert_eq!(
            media.frame_rate(),
            pixelflow_core::FrameRate::Cfr(pixelflow_core::Rational {
                numerator: 24_000,
                denominator: 1_001,
            })
        );
    }

    #[test]
    fn synthetic_u8_frame_builds_deterministic_rows() {
        let frame = synthetic_u8_frame("gray8", 3, 2, |_plane, x, y| {
            u8::try_from(x + y * 10).expect("fixture sample fits u8")
        })
        .expect("synthetic frame should build");

        pixelflow_test_support::assert_plane_u8_near(
            &frame,
            0,
            &[&[0, 1, 2], &[10, 11, 12]],
            EXACT_GOLDEN_TOLERANCE,
        );
    }

    #[test]
    fn golden_assertion_allows_declared_u8_tolerance() {
        let frame = synthetic_u8_frame("gray8", 2, 1, |_plane, x, _y| {
            u8::try_from(x).expect("fixture sample fits u8")
        })
        .expect("synthetic frame should build");

        pixelflow_test_support::assert_plane_u8_near(
            &frame,
            0,
            &[&[1, 2]],
            GoldenTolerance {
                u8_abs: 1,
                u16_abs: 0,
                f32_abs: 0.0,
            },
        );
    }

    #[test]
    fn synthetic_u16_frame_uses_u16_storage() {
        let frame = super::synthetic_u16_frame("gray10", 2, 1, |_plane, x, _y| {
            100 + u16::try_from(x).expect("fixture sample fits u16")
        })
        .expect("synthetic frame should build");

        pixelflow_test_support::assert_plane_u16_near(
            &frame,
            0,
            &[&[100, 101]],
            EXACT_GOLDEN_TOLERANCE,
        );
    }

    #[test]
    fn synthetic_f32_frame_uses_f32_storage() {
        let frame = super::synthetic_f32_frame("grayf32", 2, 1, |_plane, x, _y| match x {
            0 => 0.25,
            1 => 1.25,
            _ => unreachable!("fixture width is 2"),
        })
        .expect("synthetic frame should build");

        pixelflow_test_support::assert_plane_f32_near(
            &frame,
            0,
            &[&[0.25, 1.25]],
            GoldenTolerance {
                u8_abs: 0,
                u16_abs: 0,
                f32_abs: 0.0,
            },
        );
    }

    #[test]
    fn metadata_helpers_set_string_and_frame_number_values() {
        let frame = synthetic_u8_frame("gray8", 1, 1, |_plane, _x, _y| 0)
            .expect("synthetic frame should build");
        let frame = super::with_string_metadata(&frame, "core:range", "full")
            .expect("range metadata should set");
        let frame =
            super::with_frame_number_metadata(&frame, 7).expect("frame number metadata should set");

        assert_eq!(
            frame.metadata().get("core:range"),
            Some(&pixelflow_core::MetadataValue::String("full".to_owned()))
        );
        assert_eq!(
            frame.metadata().get("core:frame_number"),
            Some(&pixelflow_core::MetadataValue::Int(7))
        );
    }

    #[test]
    fn with_none_metadata_resets_core_key_to_none() {
        let frame = synthetic_u8_frame("gray8", 1, 1, |_plane, _x, _y| 0)
            .expect("synthetic frame should build");
        let frame = super::with_string_metadata(&frame, "core:range", "full")
            .expect("range metadata should set");
        let frame =
            super::with_none_metadata(&frame, "core:range").expect("range metadata should reset");

        assert_eq!(
            frame.metadata().get("core:range"),
            Some(&pixelflow_core::MetadataValue::None)
        );
    }
}