#[macro_use]
extern crate derive_error;
extern crate byteorder;
use std::io::{Read, Seek, SeekFrom};
use byteorder::{BigEndian, ReadBytesExt};
use std::borrow::Cow;
use std::cmp;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
pub mod create;
pub mod data;
pub mod parser;
use crate::data::{
color::ColorType, error::Error, header::XcfHeader, layer::Layer, pixeldata::PixelData,
precision::Precision, property::PropertyPayload, rgba::RgbaPixel, version::Version, xcf::Xcf,
};
use crate::parser::ParseVersion;
use crate::data::property::{Property, PropertyIdentifier};
impl Precision {
fn parse<R: Read>(mut rdr: R, version: Version) -> Result<Self, Error> {
let precision = rdr.read_u32::<BigEndian>()?;
Ok(match version.num() {
4 => match precision {
0 => Precision::NonLinearU8,
1 => Precision::NonLinearU16,
2 => Precision::LinearU32,
3 => Precision::LinearF16,
4 => Precision::LinearF32,
_ => return Err(Error::InvalidPrecision),
},
5..=6 => match precision {
100 => Precision::LinearU8,
150 => Precision::NonLinearU8,
200 => Precision::LinearU16,
250 => Precision::NonLinearU16,
300 => Precision::LinearU32,
350 => Precision::NonLinearU32,
400 => Precision::LinearF16,
450 => Precision::NonLinearF16,
500 => Precision::LinearF32,
550 => Precision::NonLinearF32,
_ => return Err(Error::InvalidPrecision),
},
7.. => match precision {
100 => Precision::LinearU8,
150 => Precision::NonLinearU8,
175 => Precision::PerceptualU8,
200 => Precision::LinearU16,
250 => Precision::NonLinearU16,
275 => Precision::PerceptualU16,
300 => Precision::LinearU32,
350 => Precision::NonLinearU32,
375 => Precision::PerceptualU32,
500 => Precision::LinearF16,
550 => Precision::NonLinearF16,
575 => Precision::PerceptualF16,
600 => Precision::LinearF32,
650 => Precision::NonLinearF32,
675 => Precision::PerceptualF32,
700 => Precision::LinearF64,
750 => Precision::NonLinearF64,
775 => Precision::PerceptualF64,
_ => return Err(Error::InvalidPrecision),
},
_ => return Err(Error::InvalidPrecision),
})
}
}
impl PropertyPayload {
fn parse<R: Read>(
mut rdr: R,
kind: PropertyIdentifier,
length: usize,
) -> Result<PropertyPayload, Error> {
use self::PropertyIdentifier::*;
Ok(match kind {
PropEnd => PropertyPayload::End,
_ => {
let mut p = vec![0; length];
rdr.read_exact(&mut p)?;
PropertyPayload::Unknown(p)
}
})
}
}
impl Layer {
fn parse<R: Read + Seek>(mut rdr: R, version: Version) -> Result<Layer, Error> {
let width = rdr.read_u32::<BigEndian>()?;
let height = rdr.read_u32::<BigEndian>()?;
let kind = LayerColorType::new(rdr.read_u32::<BigEndian>()?)?;
let name = read_gimp_string(&mut rdr)?;
let properties = Property::parse_list(&mut rdr)?;
let hptr = rdr.read_uint::<BigEndian>(version.bytes_per_offset())?;
let current_pos = rdr.stream_position()?;
rdr.seek(SeekFrom::Start(hptr))?;
let pixels = PixelData::parse_hierarchy(&mut rdr, version)?;
rdr.seek(SeekFrom::Start(current_pos))?;
Ok(Layer {
width,
height,
kind,
name,
properties,
pixels,
})
}
pub fn pixel(&self, x: u32, y: u32) -> Option<RgbaPixel> {
self.pixels.pixel(x, y)
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn raw_rgba_buffer(&self) -> Cow<[RgbaPixel]> {
Cow::from(&self.pixels.pixels)
}
pub fn raw_sub_rgba_buffer(&self, x: u32, y: u32, width: u32, height: u32) -> Vec<u8> {
self.pixels.raw_sub_rgba_buffer(x, y, width, height)
}
}
#[repr(u32)]
#[derive(Debug, PartialEq, Clone)]
pub enum LayerColorValue {
Rgb = 0,
Rgba = 1,
Grayscale = 2,
GrayscaleWithAlpha = 3,
Indexed = 4,
IndexedWithAlpha = 5
}
impl LayerColorValue {
pub(crate) fn new(kind: u32) -> Result<LayerColorValue, Error> {
use self::LayerColorValue::*;
Ok(match kind {
0 => Rgb,
1 => Rgba,
2 => Grayscale,
3 => GrayscaleWithAlpha,
4 => Indexed,
5 => Self::IndexedWithAlpha,
_ => return Err(Error::InvalidFormat),
})
}
pub(crate) fn has_alpha(value: LayerColorValue) -> bool {
match value {
LayerColorValue::Rgba | LayerColorValue::GrayscaleWithAlpha | LayerColorValue::IndexedWithAlpha => true,
LayerColorValue::Rgb | LayerColorValue::Grayscale | LayerColorValue::Indexed => false
}
}
}
#[derive(Debug, PartialEq)]
pub struct LayerColorType {
pub kind: LayerColorValue,
pub alpha: bool,
}
impl LayerColorType {
fn new(identifier: u32) -> Result<LayerColorType, Error> {
let kind = LayerColorValue::new(identifier / 2)?;
let alpha = identifier % 2 == 1;
Ok(LayerColorType { alpha, kind })
}
}
pub struct TileCursor {
width: u32,
height: u32,
channels: u32,
x: u32,
y: u32,
i: u32,
}
impl TileCursor {
fn new(width: u32, height: u32, tx: u32, ty: u32, channels: u32) -> TileCursor {
TileCursor {
width,
height,
channels,
x: tx * 64,
y: ty * 64,
i: 0,
}
}
fn feed<R: Read>(&mut self, mut rdr: R, pixels: &mut [RgbaPixel]) -> Result<(), Error> {
let twidth = cmp::min(self.x + 64, self.width) - self.x;
let theight = cmp::min(self.y + 64, self.height) - self.y;
let base_offset = self.y * self.width + self.x;
let mut channel = 0;
while channel < self.channels {
while self.i < twidth * theight {
let determinant = rdr.read_u8()?;
if determinant < 127 {
let run = u32::from(determinant + 1);
let v = rdr.read_u8()?;
for i in (self.i)..(self.i + run) {
let index = base_offset + (i / twidth) * self.width + i % twidth;
pixels[index as usize].0[channel as usize] = v;
}
self.i += run;
} else if determinant == 127 {
let run = u32::from(rdr.read_u16::<BigEndian>()?);
let v = rdr.read_u8()?;
for i in (self.i)..(self.i + run) {
let index = base_offset + (i / twidth) * self.width + i % twidth;
pixels[index as usize].0[channel as usize] = v;
}
self.i += run;
} else if determinant == 128 {
let stream_run = u32::from(rdr.read_u16::<BigEndian>()?);
for i in (self.i)..(self.i + stream_run) {
let index = base_offset + (i / twidth) * self.width + i % twidth;
let v = rdr.read_u8()?;
pixels[index as usize].0[channel as usize] = v;
}
self.i += stream_run;
} else {
let stream_run = 256 - u32::from(determinant);
for i in (self.i)..(self.i + stream_run) {
let index = base_offset + (i / twidth) * self.width + i % twidth;
let v = rdr.read_u8()?;
pixels[index as usize].0[channel as usize] = v;
}
self.i += stream_run;
}
}
self.i = 0;
channel += 1;
}
Ok(())
}
}
fn read_gimp_string<R: Read>(mut rdr: R) -> Result<String, Error> {
let length = rdr.read_u32::<BigEndian>()?;
let mut buffer = vec![0; length as usize - 1];
rdr.read_exact(&mut buffer)?;
rdr.read_exact(&mut [0u8])?;
Ok(String::from_utf8(buffer)?)
}
impl Xcf {
pub fn open<P: AsRef<Path>>(p: P) -> Result<Xcf, Error> {
let rdr = BufReader::new(File::open(p)?);
Xcf::load(rdr)
}
pub fn load<R: Read + Seek>(mut rdr: R) -> Result<Xcf, Error> {
let header = XcfHeader::parse(&mut rdr)?;
let mut layers = Vec::new();
loop {
let layer_pointer = rdr.read_uint::<BigEndian>(header.version.bytes_per_offset())?;
if layer_pointer == 0 {
break;
}
let current_pos = rdr.stream_position()?;
rdr.seek(SeekFrom::Start(layer_pointer))?;
layers.push(Layer::parse(&mut rdr, header.version)?);
rdr.seek(SeekFrom::Start(current_pos))?;
}
Ok(Xcf { header, layers })
}
pub fn width(&self) -> u32 {
self.header.width
}
pub fn height(&self) -> u32 {
self.header.height
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width(), self.height())
}
pub fn layer(&self, name: &str) -> Option<&Layer> {
self.layers.iter().find(|l| l.name == name)
}
pub fn layer_mut(&mut self, name: &str) -> Option<&mut Layer> {
self.layers.iter_mut().find(|l| l.name == name)
}
}
impl XcfHeader {
fn parse<R: Read>(mut rdr: R) -> Result<XcfHeader, Error> {
let mut magic = [0u8; 9];
rdr.read_exact(&mut magic)?;
if magic != *b"gimp xcf " {
return Err(Error::InvalidFormat);
}
let version = Version::parse(&mut rdr)?;
rdr.read_exact(&mut [0u8])?;
let width = rdr.read_u32::<BigEndian>()?;
let height = rdr.read_u32::<BigEndian>()?;
let color_type = ColorType::new(rdr.read_u32::<BigEndian>()?)?;
let precision = if version.num() >= 4 {
Precision::parse(&mut rdr, version)?
} else {
Precision::NonLinearU8
};
let properties = Property::parse_list(&mut rdr)?;
Ok(XcfHeader {
version,
width,
height,
color_type,
precision,
properties,
})
}
}