1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
extern crate byteorder;
#[macro_use] extern crate failure;
extern crate image;
pub mod footer;
use self::footer::JpngFooter;
use failure::Error;
use image::DynamicImage;
use std::mem;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
#[derive(Debug, Fail)]
pub enum JpngError {
#[fail(display = "Input is not a valid JPNG image.")]
InvalidImage,
#[fail(display = "Invalid footer length given.")]
InvalidFooterLen,
}
#[derive(Clone)]
pub struct Jpng {
image: DynamicImage,
mask: DynamicImage,
pub footer: JpngFooter,
}
impl Jpng {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Jpng, Error> {
let mut contents = Vec::new();
let footer_size = mem::size_of::<JpngFooter>();
File::open(path)?.read_to_end(&mut contents)?;
ensure!(contents.len() > footer_size, JpngError::InvalidImage);
let footer = JpngFooter::new(&contents[contents.len() - footer_size..])?;
let image = image::load_from_memory(&contents[footer.image_range()])?;
let mask = image::load_from_memory(&contents[footer.mask_range()])?;
Ok(Self {
image,
mask,
footer,
})
}
pub fn save(&self, basename: &str) -> Result<(), Error> {
let luma = self.mask.to_luma();
let mut combined = self.image.to_rgba();
self.image
.to_rgba()
.enumerate_pixels()
.zip(luma.pixels())
.map(|((x, y, old), luma)| {
combined.put_pixel(x, y,
image::Rgba {
data: [old[0], old[1], old[2], luma[0]]
});
}).collect::<()>();
combined.save(&format!("{}.png", basename))?;
Ok(())
}
pub fn save_image(&self, basename: &str) -> Result<(), Error> {
self.image.save(&format!("{}.jpg", basename))?;
Ok(())
}
pub fn save_mask(&self, basename: &str) -> Result<(), Error> {
self.mask.save(&format!("{}.png", basename))?;
Ok(())
}
}