mirx 0.45.0

MIRX binary image format — flat & chunk-based container for ahead-of-time UI assets
Documentation
use super::*;
use crate::{
    coding::Rle,
    font::{FontGlyphs, FontRepresentation, FontView},
    image::{UnitGroup, UnitSelection},
    types::Fixed,
};

fn asset(check: impl FnOnce(FontAsset<'_>)) {
    let map = GlyphMap::cells(2, 2, 2).unwrap();
    let raw = RawGlyphs::builder(map, SampleLayout::A8)
        .build(&[7; 8])
        .unwrap();
    let image = EncodedImageAsset::new(
        SurfaceDescriptor::new(2, 4, SampleLayout::A8, ColorDescription::NONE).unwrap(),
        Rle::new().record(),
        &[0x87, 42],
    )
    .with_input_alignment(crate::ByteAlignment::new(64).unwrap())
    .with_integrity(DataIntegrity::Indexed(&[1, 2]));
    let surfaces = [
        GlyphSurfaceAsset::raw(raw),
        GlyphSurfaceAsset::Encoded { map, image },
    ];
    let representations = [
        RepresentationAsset::new(FontRepresentation::coverage(8, 12, 8).unwrap(), 0),
        RepresentationAsset::new(
            FontRepresentation::signed_distance(8, 3, 24, 17, 48, 8).unwrap(),
            1,
        ),
    ];
    let face = FontFace::new(
        1_000,
        GlyphId::NOTDEF,
        2,
        Fixed::ONE,
        Fixed::ZERO,
        Fixed::ZERO,
    )
    .unwrap();
    let cmap = [
        CmapEntry::new('A', GlyphId::new(0)),
        CmapEntry::new('B', GlyphId::new(1)),
    ];
    let advances = [Fixed::ONE; 2];
    let raster_metrics = [RasterMetrics::default(); 4];
    check(
        FontAsset::new(face, &cmap, FontAdvanceSource::Advances(&advances)).with_rasters(
            &representations,
            &raster_metrics,
            &surfaces,
        ),
    );
}

fn stored_data(surface: GlyphSurfaceAsset<'_>) -> &[u8] {
    match surface {
        GlyphSurfaceAsset::Raw { glyphs, .. } => glyphs.as_bytes(),
        GlyphSurfaceAsset::Encoded { image, .. } => image.data(),
    }
}

#[test]
fn owned_metadata_edits_preserve_encoded_data_and_shared_ordinals() {
    let mut result = None;
    asset(|asset| {
        let owned = Font::from_asset(asset, &PayloadLimits::EMBEDDED).unwrap();
        assert_eq!(owned.encode().unwrap(), asset.encode().unwrap());
        result = Some(owned);
    });
    let mut font = result.unwrap();
    let source = font.encode().unwrap();
    assert!(font.matches_payload(&source).unwrap());
    let stored = stored_data(font.surface(1).unwrap()).to_vec();
    assert_eq!(stored, [0x87, 42]);
    let metric = RasterMetrics::new(Fixed::from_ratio(-17, 256), Fixed::from_ratio(-65, 256));
    font.raster_metrics_mut()[2] = metric;
    font.advances_mut().unwrap()[0] = Fixed::from_ratio(513, 256);
    font.set_cmap_entry(1, CmapEntry::new('中', GlyphId::new(1)))
        .unwrap();
    assert!(
        font.set_cmap_entry(0, CmapEntry::new('中', GlyphId::new(0)))
            .is_err()
    );
    assert!(
        font.set_cmap_entry(usize::MAX, CmapEntry::new('x', GlyphId::new(0)))
            .is_err()
    );
    assert_eq!(
        font.cmap()
            .iter()
            .map(|entry| entry.scalar())
            .collect::<Vec<_>>(),
        ['A', '中']
    );
    assert_eq!(stored_data(font.surface(1).unwrap()), stored);
    font.preflight(&PayloadLimits::EMBEDDED).unwrap();
    let bytes = font.encode().unwrap();
    let view = FontView::open_at(&bytes, 0, &PayloadLimits::EMBEDDED).unwrap();
    view.preflight(&PayloadLimits::EMBEDDED).unwrap();
    let representation = view.representation(1).unwrap();
    assert_eq!(representation.raster_metrics(GlyphId::new(0)), Some(metric));
    assert_eq!(
        representation.advance(GlyphId::new(0)),
        Some(Fixed::from_ratio(513, 256))
    );
    assert!(matches!(view.glyphs(1), Some(FontGlyphs::Encoded(_))));
    let mut output = alloc::vec![0xcc; font.encoded_len().unwrap() + 8];
    let len = font.encode_into(&mut output).unwrap();
    assert_eq!(&output[..len], bytes);
    assert_eq!(&output[len..], &[0xcc; 8]);
}

#[test]
fn semantic_image_groups_survive_font_emission_and_ownership() {
    let map = GlyphMap::cells(2, 2, 2).unwrap();
    let surface = SurfaceDescriptor::new(2, 4, SampleLayout::A8, ColorDescription::NONE).unwrap();
    let first_data = [0x83, 42];
    let second_data = [0x83, 43];
    let first_cell = [0];
    let second_cell = [1];
    let groups = [
        UnitGroup::builder(surface, Rle::new().record(), &first_data)
            .with_tiles(2, 2)
            .with_selection(UnitSelection::cells(2, &first_cell).unwrap())
            .build()
            .unwrap(),
        UnitGroup::builder(surface, Rle::new().record(), &second_data)
            .with_tiles(2, 2)
            .with_selection(UnitSelection::cells(2, &second_cell).unwrap())
            .with_input_alignment(crate::ByteAlignment::new(64).unwrap())
            .build()
            .unwrap(),
    ];
    let image = EncodedImageAsset::from_groups(surface, &groups)
        .with_integrity(DataIntegrity::Indexed(&[64, 66]));
    let surfaces = [GlyphSurfaceAsset::Encoded { map, image }];
    let representations = [RepresentationAsset::new(
        FontRepresentation::coverage(8, 12, 8).unwrap(),
        0,
    )];
    let face = FontFace::new(
        1_000,
        GlyphId::NOTDEF,
        2,
        Fixed::ONE,
        Fixed::ZERO,
        Fixed::ZERO,
    )
    .unwrap();
    let cmap = [
        CmapEntry::new('A', GlyphId::new(0)),
        CmapEntry::new('B', GlyphId::new(1)),
    ];
    let advances = [Fixed::ONE; 2];
    let metrics = [RasterMetrics::default(); 2];
    let asset = FontAsset::new(face, &cmap, FontAdvanceSource::Advances(&advances)).with_rasters(
        &representations,
        &metrics,
        &surfaces,
    );
    asset.preflight(&PayloadLimits::EMBEDDED).unwrap();
    let bytes = asset.encode().unwrap();
    let font = Font::from_asset(asset, &PayloadLimits::EMBEDDED).unwrap();
    assert_eq!(font.encode().unwrap(), bytes);
    font.preflight(&PayloadLimits::EMBEDDED).unwrap();
    FontView::open_at(&bytes, 0, &PayloadLimits::EMBEDDED)
        .unwrap()
        .preflight(&PayloadLimits::EMBEDDED)
        .unwrap();
}

#[test]
fn owning_limits_cover_metadata_and_encoded_storage_before_allocation() {
    asset(|asset| {
        assert!(matches!(
            Font::from_asset(asset, &PayloadLimits::EMBEDDED.with_max_decoded_bytes(8)),
            Err(FontError::OwnedBytesLimitExceeded { .. })
        ));
        let needed = (8..4096)
            .find(|&limit| {
                Font::from_asset(
                    asset,
                    &PayloadLimits::EMBEDDED.with_max_decoded_bytes(limit),
                )
                .is_ok()
            })
            .unwrap();
        assert!(
            Font::from_asset(
                asset,
                &PayloadLimits::EMBEDDED.with_max_decoded_bytes(needed - 1)
            )
            .is_err()
        );
        let font = Font::from_asset(
            asset,
            &PayloadLimits::EMBEDDED.with_max_decoded_bytes(needed),
        )
        .unwrap();
        assert_eq!(font.representation_count(), 2);
        assert_eq!(font.surface_count(), 2);
        assert!(font.surface(usize::MAX).is_none());
        assert!(font.representation(usize::MAX).is_none());
    });
}

#[test]
fn wire_owned_round_trip_preserves_raw_and_encoded_integrity_partitions() {
    asset(|asset| {
        let original = asset.encode().unwrap();
        let font = Font::decode_with_limits(&original, &PayloadLimits::EMBEDDED).unwrap();
        assert_eq!(font.encode().unwrap(), original);
        assert!(font.matches_payload(&original).unwrap());
        assert_eq!(
            font.surface(0).unwrap().integrity(),
            DataIntegrity::Indexed(&[8])
        );
        assert_eq!(
            font.surface(1).unwrap().integrity(),
            DataIntegrity::Indexed(&[1, 2])
        );
        for limits in [
            PayloadLimits::EMBEDDED.with_max_decoded_bytes(8),
            PayloadLimits::EMBEDDED.with_max_raster_work(0),
            PayloadLimits::EMBEDDED.with_max_font_glyphs(1),
        ] {
            assert!(Font::decode_with_limits(&original, &limits).is_err());
        }
    });
    let cmap = [
        CmapEntry::new('A', GlyphId::new(0)),
        CmapEntry::new('B', GlyphId::new(1)),
    ];
    let map = GlyphMap::cells(2, 2, 2).unwrap();
    let raw = RawGlyphs::builder(map, SampleLayout::A8)
        .build(&[7; 8])
        .unwrap();
    let representations = [RepresentationAsset::new(
        FontRepresentation::coverage(8, 12, 8).unwrap(),
        0,
    )];
    let surfaces = [GlyphSurfaceAsset::raw(raw).with_integrity(DataIntegrity::Indexed(&[4, 8]))];
    let face = FontFace::new(
        1_000,
        GlyphId::NOTDEF,
        2,
        Fixed::ONE,
        Fixed::ZERO,
        Fixed::ZERO,
    )
    .unwrap();
    let advances = [Fixed::ONE; 2];
    let raster_metrics = [RasterMetrics::default(); 2];
    let asset = FontAsset::new(face, &cmap, FontAdvanceSource::Advances(&advances)).with_rasters(
        &representations,
        &raster_metrics,
        &surfaces,
    );
    let original = asset.encode().unwrap();
    let font = Font::decode_with_limits(&original, &PayloadLimits::EMBEDDED).unwrap();
    assert_eq!(font.encode().unwrap(), original);
    assert_eq!(
        font.surface(0).unwrap().integrity(),
        DataIntegrity::Indexed(&[4, 8])
    );
    let bad_surfaces = [GlyphSurfaceAsset::raw(raw).with_integrity(DataIntegrity::Indexed(&[3]))];
    let bad = FontAsset::new(face, &cmap, FontAdvanceSource::Advances(&advances)).with_rasters(
        &representations,
        &raster_metrics,
        &bad_surfaces,
    );
    let mut output = [0xcc; 512];
    assert!(matches!(
        bad.encode_into(&mut output),
        Err(FontError::Integrity(_))
    ));
    assert_eq!(output, [0xcc; 512]);
}