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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
// "aci_ppm" - Aldaron's Codec Interface / PPM // // Copyright Jeron A. Lau 2017-2018. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) // //! Aldaron's Codec Interface / PPM is a small library developed by Plop Grizzly //! to encode and decode ppm/pnm image files. // // # MMD (Multi-Media Deflated) // MMD is a compressed (DEFLATE) variant of PNM, developed by Plop Grizzly. // // ## Initial Chunk Options --- // // ### "5" - Image - 1 byte per channel RGB or all channels off // * Little endian unsigned 16-bit width. // * Little endian unsigned 16-bit height. // * Repeating [0 or 1RGB] sequences. // // ### "4" - Image - 1 byte per channel RGBA // * Little endian unsigned 16-bit width. // * Little endian unsigned 16-bit height. // * Repeating [RGBA] 4 byte sequences. // // ### "3" - Image - 1 byte per channel RGB // * Little endian unsigned 16-bit width. // * Little endian unsigned 16-bit height. // * Repeating [RGB] 3 byte sequences. // // ### "2" - Image - 1 bit per pixel - Black & White / On & Off. // * Little endian unsigned 16-bit width. // * Little endian unsigned 16-bit height. // * Repeating bit per pixel (B&W). // // ### "1" - Image - 1 byte per pixel - Grayscale // * Little endian unsigned 16-bit width. // * Little endian unsigned 16-bit height. // * Repeating byte per pixel (Grayscale). // // ## Additional Chunks // // ### "0" - Image - Size & Channels inherited from previous image. // * Repeating byte sequences. // // ### "D" - Delay // * Little endian unsigned 32-bit delay 48000=1fps, 1600=30fps, 0=restart vid. // // ### "A" - Audio // * Little endian unsigned 32-bit delay 48000=1fps, 1600=30fps, 0=ERROR. // * 16-bit Samples equal to 32-bit delay (48000hz). // // ### "t" - Text / Title(name) // ### "c" - Creation Year / Optional Full Date // ### "a" - Artist Name // ### "w" - Artist Website // ### "p" - Publisher // ### "d" - Publish Date // ### "s" - Publisher Website // ### "l" - License: Copyright ⓒ All Rights Reserved Copyright Holder Name // * Little endian unsigned 32-bit length. // * Unicode #![warn(missing_docs)] #![doc(html_logo_url="https://plopgrizzly.com/images/plopgrizzly-splash.png", html_favicon_url="https://plopgrizzly.com/images/plopgrizzly-splash.png")] extern crate afi; extern crate byteorder; use std::io::{ Write, Read, Seek, Cursor, SeekFrom }; use afi::*; /// Encoder for PNM. pub struct PnmEncoder { // Image size. wh: (u16,u16), // 3(false) or 4(true) channels. format: ColorChannels, } impl EncoderV for PnmEncoder { fn new(video: &Video) -> PnmEncoder { let wh = video.wh(); let format = video.format(); PnmEncoder { wh, format } } fn run(&mut self, frame: &VFrame) -> Vec<u8> { let mut out = Vec::new(); out.write_fmt(format_args!("P6\n{}\n{}\n255\n", self.wh.0, self.wh.1)).unwrap(); for i in 0..((self.wh.0 as usize) * (self.wh.1 as usize)) { let [r, g, b, _] = frame.sample_rgba(self.format, i); out.write_all(&[r, g, b]).unwrap(); } out } fn end(self) -> Vec<u8> { vec![] } } /// Decoder for PPM/PNM. pub struct PnmDecoder<T: Read + Seek> { #[allow(unused)] // TODO data: T, channels: ColorChannels, wh: (u16, u16), n_frames: u32, } impl<T> Decoder<T> for PnmDecoder<T> where T: Read + Seek { fn new(mut data: T, channels: afi::ColorChannels) -> Option<PnmDecoder<T>> { // Check Header: Is PNM (PPM, TODO: other PNM formats) let mut header = [0u8; 2]; data.read(&mut header).unwrap(); match &header { b"P6" => { /* P6 = PPM = RGB */ } _ => { /* File ain't be PNM */ return None; } } // Skip Optional Comment let mut optional_comment = [0u8; 1]; data.read(&mut optional_comment).unwrap(); match &optional_comment { b"#" => { skip(&mut data); } _ => { data.seek(SeekFrom::Current(-1)).unwrap(); } } // Get Width & Height let width = utf8_to_u16(&mut data)?; let height = utf8_to_u16(&mut data)?; let max = utf8_to_u16(&mut data)?; let wh = (width, height); // Max pixel value should be 255. if max != 255 { println!("WARNING: PNM decoder requires 255 max"); return None; } // TODO: Calculate chunks from remaining file size. let n_frames = 1; Some(PnmDecoder { data, channels, wh, n_frames }) } fn run(&mut self, audio: &mut Option<Audio>, video: &mut Option<Video>) -> Option<bool> { if audio.is_none() && video.is_none() { *video = Some(Video::new( self.channels, self.wh, self.n_frames )); // First run, initialize structs, some left. Some(true) } else { // Non-first run, decode 1 frame. let ch = self.channels.n_channels(); let size = self.wh.0 as usize * self.wh.1 as usize * ch; let mut out: Vec<u8> = Vec::with_capacity(size); // Build Graphic for _ in 0..size { let mut rgb = [0u8; 3]; self.data.read(&mut rgb).unwrap(); let rgba = self.channels.from(Rgba, [rgb[0], rgb[1], rgb[2], 255u8]); for i in 0..ch { out.push(rgba[i]); } } let video = video.as_mut().unwrap(); // Isn't none, so fine. video.add(VFrame(out)); // TODO, return True if APNM and has more frames. Some(false) } } fn get(&self) -> Index { // TODO Index(0) } fn set(&mut self, _index: Index) { // TODO } } /// Simple API for quickly loading entire files all at once. pub fn decode(file: &[u8], channels: ColorChannels) -> Option<Video> { let mut image = None; let mut decoder = PnmDecoder::new(Cursor::new(file), channels)?; decoder.run(&mut None, &mut image).unwrap(); decoder.run(&mut None, &mut image).unwrap(); image } /// Skip until the next whitespace. fn skip<T>(data: &mut T) where T: Read + Seek { loop { let mut character = [0u8; 1]; data.read(&mut character).unwrap(); match &character { b"\n" | b" " | b"\t" => break, _ => { /* continue */ } } } } /// Read number until whitespace. fn utf8_to_u16<T>(data: &mut T) -> Option<u16> where T: Read + Seek { let mut number = 0; let zero = b'0'; loop { let mut character = [0u8; 1]; data.read(&mut character).unwrap(); match &character { b"\n" | b" " | b"\t" => break, digit => { let digit = digit[0]; number *= 10; if digit != zero { if digit < zero || digit > zero + 9 { return None; } number += (digit - zero) as u16; } } } } Some(number) } /*/// Encode an MMD Image pub fn encode_mmd(mut graphic: Graphic, alpha: bool) -> Vec<u8> { // Convert to RGBA bytes. graphic.rgba(); let graphic = graphic.as_bytes(); // Build the encoded data. let mut enc = DeflateEncoder::new(Vec::new(), Compression::Best); if alpha { enc.write_all(b"4").unwrap(); } else { enc.write_all(b"3").unwrap(); } enc.write_u16::<byteorder::LittleEndian>(graphic.0).unwrap(); enc.write_u16::<byteorder::LittleEndian>(graphic.1).unwrap(); if alpha { enc.write_all(graphic.2).unwrap(); } else { let size = graphic.0 as usize * graphic.1 as usize; for i in 0..size { enc.write_all(&[graphic.2[i * 4 + 0], graphic.2[i * 4 + 1], graphic.2[i * 4 + 2]] ).unwrap(); } } enc.finish().unwrap() } /// Decode an MMD Image pub fn decode_mmd(ppm: &[u8]) -> Result<Graphic, ()> { // Decompress the data. let dec = inflate_bytes(ppm).unwrap(); let mut cur = ::std::io::Cursor::new(dec); // Header - First is always image let mut header = [0; 1]; cur.read(&mut header).unwrap(); let alpha = match header { [b'4'] => true, [b'3'] => false, _ => return Err(()) }; // Width & Height let width = cur.read_u16::<byteorder::LittleEndian>().unwrap(); let height = cur.read_u16::<byteorder::LittleEndian>().unwrap(); // Pixels let size = width as usize * height as usize; let mut out: Vec<u32> = Vec::with_capacity(size); if alpha { for _ in 0..size { out.push(cur.read_u32::<byteorder::LittleEndian>().unwrap()); } } else { let mut buf = [0; 3]; for _ in 0..size { cur.read(&mut buf).unwrap(); out.push(std::io::Cursor::new([buf[0], buf[1], buf[2], 255u8]) .read_u32::<byteorder::LittleEndian>() .unwrap() ); } } // Graphic Ok(GraphicBuilder::new().rgba(width, height, out)) }*/