dmio 0.1.2

A shared library providing functionality to read, write and modify files saved in the DigitalMicrograph file format (version 3 or 4)
Documentation
/*
 * Copyright 2018 Christian Ebner
 *
 * This file is part of dmio.
 *
 * dmio is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * dmio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with dmio.  If not, see <http://www.gnu.org/licenses/>.
 */

use super::taggroup_entries::{Key, Tag, TagGroup};
use super::taggroup_entries::Tag::*;
use super::dmtypes::{SORTED, OPEN};

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
const AUTHORS: Option<&'static str> = option_env!("CARGO_PKG_AUTHORS");
const PKGNAME: Option<&'static str> = option_env!("CARGO_PKG_NAME");

/// The functions provided in this module are used to create a basic DM image root TagGroup.
/// It is not clear if this is all needed, the code is quite messy and needs refactoring.
/// For now it works, but probably will change in the future so it's better to not relay
/// on these functions.
pub fn document_object_list() -> Tag {
    let mut dol = TagGroup::new(!SORTED, !OPEN);
    let mut tg0 = TagGroup::new(SORTED, !OPEN);
    let agl = TagGroup::new(!SORTED, OPEN);
    tg0.insert(Key::from_str("AnnotationGroupList"), TagGroupEntry(agl));
    tg0.insert(Key::from_str("AnnotationType"), Long(20));
    tg0.insert(
        Key::from_str("BackgroundColor"),
        Struct(vec![Short(-1), Short(-1), Short(-1)]),
    );
    tg0.insert(Key::from_str("BackgroundMode"), Short(2));
    tg0.insert(Key::from_str("FillMode"), Short(1));
    tg0.insert(
        Key::from_str("ForegroundColor"),
        Struct(vec![Short(-1), Short(0), Short(0)]),
    );
    tg0.insert(Key::from_str("HasBackground"), Boolean(true));
    tg0.insert(Key::from_str("ImageDisplayInfo"), image_display_info());
    tg0.insert(Key::from_str("ImageDisplayType"), Long(1));
    tg0.insert(Key::from_str("ImageSource"), ULong(0));
    tg0.insert(Key::from_str("IsMoveable"), Boolean(true));
    tg0.insert(Key::from_str("IsResizable"), Boolean(true));
    tg0.insert(Key::from_str("IsSelectable"), Boolean(true));
    tg0.insert(Key::from_str("IsTranslatable"), Boolean(true));
    tg0.insert(Key::from_str("IsVisible"), Boolean(true));
    let ot = TagGroup::new(SORTED, !OPEN);
    tg0.insert(Key::from_str("ObjectTags"), TagGroupEntry(ot));
    tg0.insert(
        Key::from_str("Rectangle"),
        Struct(vec![Float(0.0), Float(0.0), Float(128.0), Float(128.0)]),
    );
    tg0.insert(Key::from_str("UniqueID"), ULong(8));
    dol.insert(Key::Index(0), TagGroupEntry(tg0));

    TagGroupEntry(dol)
}

fn image_display_info() -> Tag {
    let mut idi = TagGroup::new(SORTED, !OPEN);
    idi.insert(
        Key::from_str("BrightColor"),
        Struct(vec![Short(-1), Short(-1), Short(-1)]),
    );
    idi.insert(Key::from_str("CaptionOn"), Boolean(false));
    idi.insert(Key::from_str("CaptionSize"), UShort(10));
    idi.insert(Key::from_str("Brightness"), Float(0.5));
    idi.insert(Key::from_str("CLUT"), new_clut());
    idi.insert(
        Key::from_str("CLUTName"),
        ArrayUShort(vec![71, 114, 101, 121, 115, 99, 97, 108, 101]),
    );
    idi.insert(Key::from_str("ComplexMode"), ULong(4));
    idi.insert(Key::from_str("ComplexRange"), Float(1000.0));
    idi.insert(Key::from_str("Contrast"), Float(0.5));
    idi.insert(Key::from_str("ContrastMode"), ULong(1));
    let mut dim_labels = TagGroup::new(!SORTED, !OPEN);
    dim_labels.insert(Key::Index(0), ArrayUShort(vec![]));
    idi.insert(Key::from_str("DimensionLabels"), TagGroupEntry(dim_labels));
    idi.insert(Key::from_str("DoAutoSurvey"), Boolean(true));
    idi.insert(Key::from_str("EstimatedMax"), Float(0.0));
    idi.insert(Key::from_str("EstimatedMaxTrimPercentage"), Float(0.001));
    idi.insert(Key::from_str("EstimatedMin"), Float(0.0));
    idi.insert(Key::from_str("EstimatedMinTrimPercentage"), Float(0.001));
    idi.insert(Key::from_str("Gamma"), Float(0.5));
    idi.insert(
        Key::from_str("HiLimitContrastDeltaTriggerPercentage"),
        Float(0.0),
    );
    idi.insert(Key::from_str("HighLimit"), Float(0.0));
    idi.insert(Key::from_str("IsInverted"), Boolean(false));
    idi.insert(Key::from_str("LowLimit"), Float(0.0));
    idi.insert(
        Key::from_str("LowLimitContrastDeltaTriggerPercentage"),
        Float(0.0),
    );
    let mut main_slice_id = TagGroup::new(!SORTED, !OPEN);
    main_slice_id.insert(Key::Index(0), ULong(0));
    idi.insert(Key::from_str("MainSliceId"), TagGroupEntry(main_slice_id));
    idi.insert(Key::from_str("MinimumContrast"), Float(0.0));
    idi.insert(Key::from_str("RangeAdjust"), Float(1.0));
    idi.insert(Key::from_str("SparseSurvey_GridSize"), ULong(16));
    idi.insert(Key::from_str("SparseSurvey_NumberPixels"), ULong(32));
    idi.insert(Key::from_str("SparseSurvey_UseNumberPixels"), Boolean(true));
    idi.insert(Key::from_str("SurveyTechique"), ULong(2));

    TagGroupEntry(idi)
}

fn new_clut() -> Tag {
    let mut clut = Vec::new();
    for i in 0..256 {
        let val = (i * 257usize) as i16;
        let st = Struct(vec![Short(val), Short(val), Short(val)]);
        clut.push(st);
    }

    ComplexArray(clut)
}

pub fn image_behavior() -> Tag {
    let mut ib = TagGroup::new(SORTED, !OPEN);
    ib.insert(Key::from_str("DoIntegralZoom"), Boolean(true));
    ib.insert(
        Key::from_str("ImageDisplayBounds"),
        Struct(vec![Float(0.0), Float(0.0), Float(128.0), Float(128.0)]),
    );
    ib.insert(Key::from_str("IsZoomedToWindow"), Boolean(false));
    let mut unscaled_transform = TagGroup::new(SORTED, !OPEN);
    unscaled_transform.insert(
        Key::from_str("Offset"),
        Struct(vec![Float(0.0), Float(0.0)]),
    );
    unscaled_transform.insert(Key::from_str("Scale"), Struct(vec![Float(1.0), Float(1.0)]));
    ib.insert(
        Key::from_str("UnscaledTransform"),
        TagGroupEntry(unscaled_transform),
    );
    ib.insert(Key::from_str("ViewDisplayID"), ULong(0));
    ib.insert(
        Key::from_str("WindowRect"),
        Struct(vec![Float(0.0), Float(0.0), Float(128.0), Float(128.0)]),
    );
    let mut zoom_and_move_transform = TagGroup::new(SORTED, !OPEN);
    zoom_and_move_transform.insert(
        Key::from_str("Offset"),
        Struct(vec![Float(0.0), Float(0.0)]),
    );
    zoom_and_move_transform.insert(Key::from_str("Scale"), Struct(vec![Float(1.0), Float(1.0)]));
    ib.insert(
        Key::from_str("ZoomAndMoveTransform"),
        TagGroupEntry(zoom_and_move_transform),
    );
    TagGroupEntry(ib)
}

pub fn image_list(shape: &[usize], image_type: u32) -> Tag {
    let mut il = TagGroup::new(!SORTED, !OPEN);
    let mut tg0 = TagGroup::new(SORTED, !OPEN);
    tg0.insert(Key::from_str("ImageData"), new_image_data(&[128, 128], 23));
    tg0.insert(Key::from_str("ImageTags"), new_image_tags());
    tg0.insert(
        Key::from_str("Name"),
        ArrayUShort(vec![73, 109, 97, 103, 101, 32, 79, 102, 32, 50, 68]),
    );
    tg0.insert(Key::from_str("UniqueID"), get_uniqueid_thumb());
    il.insert(Key::Index(0), TagGroupEntry(tg0));
    let mut tg1 = TagGroup::new(SORTED, !OPEN);
    tg1.insert(
        Key::from_str("ImageData"),
        new_image_data(shape, image_type),
    );
    tg1.insert(Key::from_str("ImageTags"), new_image_tags());
    tg1.insert(Key::from_str("Name"), ArrayUShort(vec![50, 68]));
    tg1.insert(Key::from_str("UniqueID"), get_uniqueid());
    il.insert(Key::Index(1), TagGroupEntry(tg1));
    TagGroupEntry(il)
}

pub fn image_source_list() -> Tag {
    let mut isl = TagGroup::new(!SORTED, !OPEN);
    let mut tg0 = TagGroup::new(SORTED, !OPEN);
    tg0.insert(
        Key::from_str("ClassName"),
        ArrayUShort(vec![
            73,
            109,
            97,
            103,
            101,
            83,
            111,
            117,
            114,
            99,
            101,
            58,
            83,
            105,
            109,
            112,
            108,
            101,
        ]),
    );
    let mut id = TagGroup::new(!SORTED, !OPEN);
    id.insert(Key::Index(0), ULong(0));
    tg0.insert(Key::from_str("Id"), TagGroupEntry(id));
    tg0.insert(Key::from_str("ImageRef"), ULong(1));
    isl.insert(Key::Index(0), TagGroupEntry(tg0));
    TagGroupEntry(isl)
}

pub fn min_version_list() -> Tag {
    let mut mvl = TagGroup::new(!SORTED, !OPEN);
    let mut tg0 = TagGroup::new(SORTED, !OPEN);
    tg0.insert(Key::from_str("RequiredVersion"), ULong(50_659_328));
    mvl.insert(Key::Index(0), TagGroupEntry(tg0));
    TagGroupEntry(mvl)
}

pub fn page_behavior() -> Tag {
    let mut pb = TagGroup::new(SORTED, !OPEN);
    pb.insert(Key::from_str("DoIntegralZoom"), Boolean(false));
    pb.insert(Key::from_str("DrawMargins"), Boolean(false));
    pb.insert(Key::from_str("DrawPaper"), Boolean(false));
    pb.insert(Key::from_str("IsFixedInPageMode"), Boolean(false));
    pb.insert(Key::from_str("IsZoomedToWindow"), Boolean(false));
    pb.insert(Key::from_str("LayedOut"), Boolean(false));
    let mut page_transform = TagGroup::new(SORTED, !OPEN);
    page_transform.insert(
        Key::from_str("Offset"),
        Struct(vec![Float(0.0), Float(0.0)]),
    );
    page_transform.insert(Key::from_str("Scale"), Struct(vec![Float(1.0), Float(1.0)]));
    pb.insert(
        Key::from_str("PageTransform"),
        TagGroupEntry(page_transform),
    );
    pb.insert(
        Key::from_str("RestoreImageDisplayBounds"),
        Struct(vec![Float(0.0), Float(0.0), Float(100.0), Float(100.0)]),
    );
    pb.insert(Key::from_str("RestoreImageDisplayID"), ULong(0));
    pb.insert(Key::from_str("TargetDisplayID"), ULong(0));
    TagGroupEntry(pb)
}

pub fn page_setup() -> Tag {
    let ps = TagGroup::new(SORTED, !OPEN);
    TagGroupEntry(ps)
}


pub fn thumbnails() -> Tag {
    let mut t = TagGroup::new(!SORTED, !OPEN);
    let mut tg0 = TagGroup::new(SORTED, !OPEN);
    tg0.insert(Key::from_str("ImageIndex"), ULong(0));
    tg0.insert(
        Key::from_str("SourceSize_Pixels"),
        Struct(vec![Long(128), Long(128)]),
    );
    t.insert(Key::Index(0), TagGroupEntry(tg0));
    TagGroupEntry(t)
}

fn new_image_data(shape: &[usize], image_type: u32) -> Tag {
    let mut image_data = TagGroup::new(SORTED, !OPEN);
    image_data.insert(Key::from_str("Calibrations"), new_calibration(shape.len()));
    let data_len = shape.iter().product();

    let data = match image_type {
        2 => ArrayFloat(vec![0.0; data_len]),
        23 => ArrayLong(vec![0; data_len]),
        _ => panic!("Unknown type"),
    };
    image_data.insert(Key::from_str("Data"), data);
    image_data.insert(Key::from_str("DataType"), ULong(image_type));
    image_data.insert(Key::from_str("Dimensions"), new_dim(shape));
    image_data.insert(Key::from_str("PixelDepth"), ULong(4));
    TagGroupEntry(image_data)
}

fn new_calibration(n_dim: usize) -> Tag {
    let mut calibrations = TagGroup::new(SORTED, !OPEN);
    calibrations.insert(Key::from_str("Brightness"), new_dim_cal());
    let mut dimension = TagGroup::new(!SORTED, !OPEN);
    for i in 0..n_dim {
        dimension.insert(Key::Index(i), new_dim_cal());
    }
    calibrations.insert(Key::from_str("Dimension"), TagGroupEntry(dimension));
    calibrations.insert(Key::from_str("DisplayCalibratedUnits"), Boolean(true));
    TagGroupEntry(calibrations)
}

fn new_dim_cal() -> Tag {
    let mut dim_cal = TagGroup::new(SORTED, !OPEN);
    dim_cal.insert(Key::from_str("Origin"), Float(0.0));
    dim_cal.insert(Key::from_str("Scale"), Float(1.0));
    dim_cal.insert(Key::from_str("Units"), ArrayUShort(Vec::new()));
    TagGroupEntry(dim_cal)
}

fn new_dim(shape: &[usize]) -> Tag {
    let mut dim = TagGroup::new(!SORTED, !OPEN);
    for (ind, &size) in shape.iter().enumerate() {
        dim.insert(Key::Index(ind), ULong(size as u32));
    }
    TagGroupEntry(dim)
}

fn new_image_tags() -> Tag {
    let mut img_tags = TagGroup::new(SORTED, !OPEN);
    let mut tg_ver = TagGroup::new(SORTED, !OPEN);
    let version = VERSION.unwrap_or("unknown").encode_utf16().collect();
    tg_ver.insert(Key::from_str("Version"), ArrayUShort(version));
    let authors = AUTHORS.unwrap_or("unknown").encode_utf16().collect();
    tg_ver.insert(Key::from_str("Authors"), ArrayUShort(authors));
    let mut description = "Image created by ".to_string();
    description.push_str(PKGNAME.unwrap_or("dm3io"));
    img_tags.insert(Key::Label(description), TagGroupEntry(tg_ver));
    TagGroupEntry(img_tags)
}

fn get_uniqueid_thumb() -> Tag {
    let mut uniqueid = TagGroup::new(!SORTED, !OPEN);
    uniqueid.insert(Key::Index(0), ULong(843_787_323));
    uniqueid.insert(Key::Index(1), ULong(725_888_012));
    uniqueid.insert(Key::Index(2), ULong(853_419_723));
    uniqueid.insert(Key::Index(3), ULong(192_158_838));
    TagGroupEntry(uniqueid)
}

fn get_uniqueid() -> Tag {
    let mut uniqueid = TagGroup::new(!SORTED, !OPEN);
    uniqueid.insert(Key::Index(0), ULong(22_810_150));
    uniqueid.insert(Key::Index(1), ULong(1_405_054_737));
    uniqueid.insert(Key::Index(2), ULong(893_145_621));
    uniqueid.insert(Key::Index(3), ULong(538_509_487));
    TagGroupEntry(uniqueid)
}