TGA (AKA Truevision TGA, AKA TARGA) raster image file format: Rust parsing library

TGA (AKA Truevision TGA, AKA TARGA), is a raster image file format created by Truevision. It supports up to 32 bits per pixel (three 8-bit RGB channels + 8-bit alpha channel), color mapping and optional lossless RLE compression.

File extension

["tga", "icb", "vda", "vst"]

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of TGA (AKA Truevision TGA, AKA TARGA) raster image file format using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Rust source code to parse TGA (AKA Truevision TGA, AKA TARGA) raster image file format

tga.rs

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#![allow(unused_imports)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(irrefutable_let_patterns)]
#![allow(unused_comparisons)]

extern crate kaitai;
use kaitai::*;
use std::convert::{TryFrom, TryInto};
use std::cell::{Ref, Cell, RefCell};
use std::rc::{Rc, Weak};

/**
 * TGA (AKA Truevision TGA, AKA TARGA), is a raster image file format created by Truevision. It supports up to 32 bits per pixel (three 8-bit RGB channels + 8-bit alpha channel), color mapping and optional lossless RLE compression.
 * \sa https://www.dca.fee.unicamp.br/~martino/disciplinas/ea978/tgaffs.pdf Source
 */

#[derive(Default, Debug, Clone)]
pub struct Tga {
    pub _root: SharedType<Tga>,
    pub _parent: SharedType<Tga>,
    pub _self: SharedType<Self>,
    image_id_len: RefCell<u8>,
    color_map_type: RefCell<Tga_ColorMapEnum>,
    image_type: RefCell<Tga_ImageTypeEnum>,
    color_map_ofs: RefCell<u16>,
    num_color_map: RefCell<u16>,
    color_map_depth: RefCell<u8>,
    x_offset: RefCell<u16>,
    y_offset: RefCell<u16>,
    width: RefCell<u16>,
    height: RefCell<u16>,
    image_depth: RefCell<u8>,
    img_descriptor: RefCell<u8>,
    image_id: RefCell<Vec<u8>>,
    color_map: RefCell<Vec<Vec<u8>>>,
    _io: RefCell<BytesReader>,
    f_footer: Cell<bool>,
    footer: RefCell<OptRc<Tga_TgaFooter>>,
}
impl KStruct for Tga {
    type Root = Tga;
    type Parent = Tga;

    fn read<S: KStream>(
        self_rc: &OptRc<Self>,
        _io: &S,
        _root: SharedType<Self::Root>,
        _parent: SharedType<Self::Parent>,
    ) -> KResult<()> {
        *self_rc._io.borrow_mut() = _io.clone();
        self_rc._root.set(_root.get());
        self_rc._parent.set(_parent.get());
        self_rc._self.set(Ok(self_rc.clone()));
        let _rrc = self_rc._root.get_value().borrow().upgrade();
        let _prc = self_rc._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        *self_rc.image_id_len.borrow_mut() = _io.read_u1()?.into();
        *self_rc.color_map_type.borrow_mut() = (_io.read_u1()? as i64).try_into()?;
        *self_rc.image_type.borrow_mut() = (_io.read_u1()? as i64).try_into()?;
        *self_rc.color_map_ofs.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.num_color_map.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.color_map_depth.borrow_mut() = _io.read_u1()?.into();
        *self_rc.x_offset.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.y_offset.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.width.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.height.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.image_depth.borrow_mut() = _io.read_u1()?.into();
        *self_rc.img_descriptor.borrow_mut() = _io.read_u1()?.into();
        *self_rc.image_id.borrow_mut() = _io.read_bytes(*self_rc.image_id_len() as usize)?.into();
        if *self_rc.color_map_type() == Tga_ColorMapEnum::HasColorMap {
            *self_rc.color_map.borrow_mut() = Vec::new();
            let l_color_map = *self_rc.num_color_map();
            for _i in 0..l_color_map {
                self_rc.color_map.borrow_mut().push(_io.read_bytes(((((*self_rc.color_map_depth() as u8) + (7 as u8)) as i32) / (8 as i32)) as usize)?.into());
            }
        }
        Ok(())
    }
}
impl Tga {
    pub fn footer(
        &self
    ) -> KResult<Ref<'_, OptRc<Tga_TgaFooter>>> {
        let _io = self._io.borrow();
        let _rrc = self._root.get_value().borrow().upgrade();
        let _prc = self._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if self.f_footer.get() {
            return Ok(self.footer.borrow());
        }
        let _pos = _io.pos();
        _io.seek(((_io.size() as i32) - (26 as i32)) as usize)?;
        let t = Self::read_into::<_, Tga_TgaFooter>(&*_io, Some(self._root.clone()), Some(self._self.clone()))?.into();
        *self.footer.borrow_mut() = t;
        _io.seek(_pos)?;
        Ok(self.footer.borrow())
    }
}
impl Tga {
    pub fn image_id_len(&self) -> Ref<'_, u8> {
        self.image_id_len.borrow()
    }
}
impl Tga {
    pub fn color_map_type(&self) -> Ref<'_, Tga_ColorMapEnum> {
        self.color_map_type.borrow()
    }
}
impl Tga {
    pub fn image_type(&self) -> Ref<'_, Tga_ImageTypeEnum> {
        self.image_type.borrow()
    }
}
impl Tga {
    pub fn color_map_ofs(&self) -> Ref<'_, u16> {
        self.color_map_ofs.borrow()
    }
}

/**
 * Number of entries in a color map
 */
impl Tga {
    pub fn num_color_map(&self) -> Ref<'_, u16> {
        self.num_color_map.borrow()
    }
}

/**
 * Number of bits in a each color maps entry
 */
impl Tga {
    pub fn color_map_depth(&self) -> Ref<'_, u8> {
        self.color_map_depth.borrow()
    }
}
impl Tga {
    pub fn x_offset(&self) -> Ref<'_, u16> {
        self.x_offset.borrow()
    }
}
impl Tga {
    pub fn y_offset(&self) -> Ref<'_, u16> {
        self.y_offset.borrow()
    }
}

/**
 * Width of the image, in pixels
 */
impl Tga {
    pub fn width(&self) -> Ref<'_, u16> {
        self.width.borrow()
    }
}

/**
 * Height of the image, in pixels
 */
impl Tga {
    pub fn height(&self) -> Ref<'_, u16> {
        self.height.borrow()
    }
}
impl Tga {
    pub fn image_depth(&self) -> Ref<'_, u8> {
        self.image_depth.borrow()
    }
}
impl Tga {
    pub fn img_descriptor(&self) -> Ref<'_, u8> {
        self.img_descriptor.borrow()
    }
}

/**
 * Arbitrary application-specific information that is used to
 * identify image. May contain text or some binary data.
 */
impl Tga {
    pub fn image_id(&self) -> Ref<'_, Vec<u8>> {
        self.image_id.borrow()
    }
}

/**
 * Color map
 */
impl Tga {
    pub fn color_map(&self) -> Ref<'_, Vec<Vec<u8>>> {
        self.color_map.borrow()
    }
}
impl Tga {
    pub fn _io(&self) -> Ref<'_, BytesReader> {
        self._io.borrow()
    }
}
#[derive(Debug, PartialEq, Clone)]
pub enum Tga_ColorMapEnum {
    NoColorMap,
    HasColorMap,
    Unknown(i64),
}

impl TryFrom<i64> for Tga_ColorMapEnum {
    type Error = KError;
    fn try_from(flag: i64) -> KResult<Tga_ColorMapEnum> {
        match flag {
            0 => Ok(Tga_ColorMapEnum::NoColorMap),
            1 => Ok(Tga_ColorMapEnum::HasColorMap),
            _ => Ok(Tga_ColorMapEnum::Unknown(flag)),
        }
    }
}

impl From<&Tga_ColorMapEnum> for i64 {
    fn from(v: &Tga_ColorMapEnum) -> Self {
        match *v {
            Tga_ColorMapEnum::NoColorMap => 0,
            Tga_ColorMapEnum::HasColorMap => 1,
            Tga_ColorMapEnum::Unknown(v) => v
        }
    }
}

impl Default for Tga_ColorMapEnum {
    fn default() -> Self { Tga_ColorMapEnum::Unknown(0) }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Tga_ImageTypeEnum {
    NoImageData,
    UncompColorMapped,
    UncompTrueColor,
    UncompBw,
    RleColorMapped,
    RleTrueColor,
    RleBw,
    Unknown(i64),
}

impl TryFrom<i64> for Tga_ImageTypeEnum {
    type Error = KError;
    fn try_from(flag: i64) -> KResult<Tga_ImageTypeEnum> {
        match flag {
            0 => Ok(Tga_ImageTypeEnum::NoImageData),
            1 => Ok(Tga_ImageTypeEnum::UncompColorMapped),
            2 => Ok(Tga_ImageTypeEnum::UncompTrueColor),
            3 => Ok(Tga_ImageTypeEnum::UncompBw),
            9 => Ok(Tga_ImageTypeEnum::RleColorMapped),
            10 => Ok(Tga_ImageTypeEnum::RleTrueColor),
            11 => Ok(Tga_ImageTypeEnum::RleBw),
            _ => Ok(Tga_ImageTypeEnum::Unknown(flag)),
        }
    }
}

impl From<&Tga_ImageTypeEnum> for i64 {
    fn from(v: &Tga_ImageTypeEnum) -> Self {
        match *v {
            Tga_ImageTypeEnum::NoImageData => 0,
            Tga_ImageTypeEnum::UncompColorMapped => 1,
            Tga_ImageTypeEnum::UncompTrueColor => 2,
            Tga_ImageTypeEnum::UncompBw => 3,
            Tga_ImageTypeEnum::RleColorMapped => 9,
            Tga_ImageTypeEnum::RleTrueColor => 10,
            Tga_ImageTypeEnum::RleBw => 11,
            Tga_ImageTypeEnum::Unknown(v) => v
        }
    }
}

impl Default for Tga_ImageTypeEnum {
    fn default() -> Self { Tga_ImageTypeEnum::Unknown(0) }
}


#[derive(Default, Debug, Clone)]
pub struct Tga_TgaExtArea {
    pub _root: SharedType<Tga>,
    pub _parent: SharedType<Tga_TgaFooter>,
    pub _self: SharedType<Self>,
    ext_area_size: RefCell<u16>,
    author_name: RefCell<String>,
    comments: RefCell<Vec<String>>,
    timestamp: RefCell<Vec<u8>>,
    job_id: RefCell<String>,
    job_time: RefCell<String>,
    software_id: RefCell<String>,
    software_version: RefCell<Vec<u8>>,
    key_color: RefCell<u32>,
    pixel_aspect_ratio: RefCell<u32>,
    gamma_value: RefCell<u32>,
    color_corr_ofs: RefCell<u32>,
    postage_stamp_ofs: RefCell<u32>,
    scan_line_ofs: RefCell<u32>,
    attributes: RefCell<u8>,
    _io: RefCell<BytesReader>,
}
impl KStruct for Tga_TgaExtArea {
    type Root = Tga;
    type Parent = Tga_TgaFooter;

    fn read<S: KStream>(
        self_rc: &OptRc<Self>,
        _io: &S,
        _root: SharedType<Self::Root>,
        _parent: SharedType<Self::Parent>,
    ) -> KResult<()> {
        *self_rc._io.borrow_mut() = _io.clone();
        self_rc._root.set(_root.get());
        self_rc._parent.set(_parent.get());
        self_rc._self.set(Ok(self_rc.clone()));
        let _rrc = self_rc._root.get_value().borrow().upgrade();
        let _prc = self_rc._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        *self_rc.ext_area_size.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.author_name.borrow_mut() = bytes_to_str(&_io.read_bytes(41 as usize)?.into(), "ASCII")?;
        *self_rc.comments.borrow_mut() = Vec::new();
        let l_comments = 4;
        for _i in 0..l_comments {
            self_rc.comments.borrow_mut().push(bytes_to_str(&_io.read_bytes(81 as usize)?.into(), "ASCII")?);
        }
        *self_rc.timestamp.borrow_mut() = _io.read_bytes(12 as usize)?.into();
        *self_rc.job_id.borrow_mut() = bytes_to_str(&_io.read_bytes(41 as usize)?.into(), "ASCII")?;
        *self_rc.job_time.borrow_mut() = bytes_to_str(&_io.read_bytes(6 as usize)?.into(), "ASCII")?;
        *self_rc.software_id.borrow_mut() = bytes_to_str(&_io.read_bytes(41 as usize)?.into(), "ASCII")?;
        *self_rc.software_version.borrow_mut() = _io.read_bytes(3 as usize)?.into();
        *self_rc.key_color.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.pixel_aspect_ratio.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.gamma_value.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.color_corr_ofs.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.postage_stamp_ofs.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.scan_line_ofs.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.attributes.borrow_mut() = _io.read_u1()?.into();
        Ok(())
    }
}
impl Tga_TgaExtArea {
}

/**
 * Extension area size in bytes (always 495)
 */
impl Tga_TgaExtArea {
    pub fn ext_area_size(&self) -> Ref<'_, u16> {
        self.ext_area_size.borrow()
    }
}
impl Tga_TgaExtArea {
    pub fn author_name(&self) -> Ref<'_, String> {
        self.author_name.borrow()
    }
}

/**
 * Comments, organized as four lines, each consisting of 80 characters plus a NULL
 */
impl Tga_TgaExtArea {
    pub fn comments(&self) -> Ref<'_, Vec<String>> {
        self.comments.borrow()
    }
}

/**
 * Image creation date / time
 */
impl Tga_TgaExtArea {
    pub fn timestamp(&self) -> Ref<'_, Vec<u8>> {
        self.timestamp.borrow()
    }
}

/**
 * Internal job ID, to be used in image workflow systems
 */
impl Tga_TgaExtArea {
    pub fn job_id(&self) -> Ref<'_, String> {
        self.job_id.borrow()
    }
}

/**
 * Hours, minutes and seconds spent creating the file (for billing, etc.)
 */
impl Tga_TgaExtArea {
    pub fn job_time(&self) -> Ref<'_, String> {
        self.job_time.borrow()
    }
}

/**
 * The application that created the file.
 */
impl Tga_TgaExtArea {
    pub fn software_id(&self) -> Ref<'_, String> {
        self.software_id.borrow()
    }
}
impl Tga_TgaExtArea {
    pub fn software_version(&self) -> Ref<'_, Vec<u8>> {
        self.software_version.borrow()
    }
}
impl Tga_TgaExtArea {
    pub fn key_color(&self) -> Ref<'_, u32> {
        self.key_color.borrow()
    }
}
impl Tga_TgaExtArea {
    pub fn pixel_aspect_ratio(&self) -> Ref<'_, u32> {
        self.pixel_aspect_ratio.borrow()
    }
}
impl Tga_TgaExtArea {
    pub fn gamma_value(&self) -> Ref<'_, u32> {
        self.gamma_value.borrow()
    }
}

/**
 * Number of bytes from the beginning of the file to the color correction table if present
 */
impl Tga_TgaExtArea {
    pub fn color_corr_ofs(&self) -> Ref<'_, u32> {
        self.color_corr_ofs.borrow()
    }
}

/**
 * Number of bytes from the beginning of the file to the postage stamp image if present
 */
impl Tga_TgaExtArea {
    pub fn postage_stamp_ofs(&self) -> Ref<'_, u32> {
        self.postage_stamp_ofs.borrow()
    }
}

/**
 * Number of bytes from the beginning of the file to the scan lines table if present
 */
impl Tga_TgaExtArea {
    pub fn scan_line_ofs(&self) -> Ref<'_, u32> {
        self.scan_line_ofs.borrow()
    }
}

/**
 * Specifies the alpha channel
 */
impl Tga_TgaExtArea {
    pub fn attributes(&self) -> Ref<'_, u8> {
        self.attributes.borrow()
    }
}
impl Tga_TgaExtArea {
    pub fn _io(&self) -> Ref<'_, BytesReader> {
        self._io.borrow()
    }
}

#[derive(Default, Debug, Clone)]
pub struct Tga_TgaFooter {
    pub _root: SharedType<Tga>,
    pub _parent: SharedType<Tga>,
    pub _self: SharedType<Self>,
    ext_area_ofs: RefCell<u32>,
    dev_dir_ofs: RefCell<u32>,
    version_magic: RefCell<Vec<u8>>,
    _io: RefCell<BytesReader>,
    f_ext_area: Cell<bool>,
    ext_area: RefCell<OptRc<Tga_TgaExtArea>>,
    f_is_valid: Cell<bool>,
    is_valid: RefCell<bool>,
}
impl KStruct for Tga_TgaFooter {
    type Root = Tga;
    type Parent = Tga;

    fn read<S: KStream>(
        self_rc: &OptRc<Self>,
        _io: &S,
        _root: SharedType<Self::Root>,
        _parent: SharedType<Self::Parent>,
    ) -> KResult<()> {
        *self_rc._io.borrow_mut() = _io.clone();
        self_rc._root.set(_root.get());
        self_rc._parent.set(_parent.get());
        self_rc._self.set(Ok(self_rc.clone()));
        let _rrc = self_rc._root.get_value().borrow().upgrade();
        let _prc = self_rc._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        *self_rc.ext_area_ofs.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.dev_dir_ofs.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.version_magic.borrow_mut() = _io.read_bytes(18 as usize)?.into();
        Ok(())
    }
}
impl Tga_TgaFooter {
    pub fn ext_area(
        &self
    ) -> KResult<Ref<'_, OptRc<Tga_TgaExtArea>>> {
        let _io = self._io.borrow();
        let _rrc = self._root.get_value().borrow().upgrade();
        let _prc = self._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if self.f_ext_area.get() {
            return Ok(self.ext_area.borrow());
        }
        if *self.is_valid()? {
            let _pos = _io.pos();
            _io.seek(*self.ext_area_ofs() as usize)?;
            let t = Self::read_into::<_, Tga_TgaExtArea>(&*_io, Some(self._root.clone()), Some(self._self.clone()))?.into();
            *self.ext_area.borrow_mut() = t;
            _io.seek(_pos)?;
        }
        Ok(self.ext_area.borrow())
    }
    pub fn is_valid(
        &self
    ) -> KResult<Ref<'_, bool>> {
        let _io = self._io.borrow();
        let _rrc = self._root.get_value().borrow().upgrade();
        let _prc = self._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if self.f_is_valid.get() {
            return Ok(self.is_valid.borrow());
        }
        self.f_is_valid.set(true);
        *self.is_valid.borrow_mut() = (*self.version_magic() == vec![0x54u8, 0x52u8, 0x55u8, 0x45u8, 0x56u8, 0x49u8, 0x53u8, 0x49u8, 0x4fu8, 0x4eu8, 0x2du8, 0x58u8, 0x46u8, 0x49u8, 0x4cu8, 0x45u8, 0x2eu8, 0x0u8]) as bool;
        Ok(self.is_valid.borrow())
    }
}

/**
 * Offset to extension area
 */
impl Tga_TgaFooter {
    pub fn ext_area_ofs(&self) -> Ref<'_, u32> {
        self.ext_area_ofs.borrow()
    }
}

/**
 * Offset to developer directory
 */
impl Tga_TgaFooter {
    pub fn dev_dir_ofs(&self) -> Ref<'_, u32> {
        self.dev_dir_ofs.borrow()
    }
}
impl Tga_TgaFooter {
    pub fn version_magic(&self) -> Ref<'_, Vec<u8>> {
        self.version_magic.borrow()
    }
}
impl Tga_TgaFooter {
    pub fn _io(&self) -> Ref<'_, BytesReader> {
        self._io.borrow()
    }
}