// SPDX-License-Identifier: LGPL-3.0-or-later /* * libopenraw - decompress.rs * * Copyright (C) 2022-2024 Hubert Figuière * * This library 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. * * This library 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 this library. If not, see * . */ //! Decompression mod ljpeg; mod sliced_buffer; mod tiled; pub use ljpeg::LJpeg; pub(crate) use tiled::TiledLJpeg; use std::io::{Read, Seek, SeekFrom}; use crate::container::RawContainer; use crate::tiff; use crate::{Error, Result}; /// Unpack n-bits into 16-bits values /// out_len is the number of expected 16-bits pixel. /// For performance `out_data` should have reserved size /// Return the number of elements written. fn unpack_bento16(input: &[u8], n: u16, out_len: usize, out_data: &mut Vec) -> Result { if n > 16 { return Err(Error::InvalidParam); } let mut reader = bitreader::BitReader::new(input); let mut written = 0_usize; for _ in 0..out_len { let t = reader.read_u16(n as u8)?; out_data.push(t); written += 1; } Ok(written) } /// Unpack 12-bits into 16-bits values BigEndian /// For performance `out_data` should have reserved size /// Return the number of elements written. pub(crate) fn unpack_be12to16( input: &[u8], out_data: &mut Vec, compression: tiff::Compression, ) -> Result { let pad = if compression == tiff::Compression::NikonPack { 1_usize } else { 0_usize }; let n = input.len() / (15 + pad); let rest = input.len() % (15 + pad); let mut src = 0_usize; // index in source if pad != 0 && (input.len() % 16) != 0 { log::error!("be12to16 incorrect padding for {:?}.", compression); return Err(Error::Decompression(format!( "be12to16 incorrect padding for {compression:?}.", ))); } if (rest % 3) != 0 { log::error!("be12to16 incorrect rest for {:?}.", compression); return Err(Error::Decompression(format!( "be12to16 incorrect rest for {compression:?}.", ))); } let mut written = 0_usize; for i in 0..=n { let m = if i == n { rest / 3 } else { 5 }; // XXX check overflow for _ in 0..m { let i0 = input[src] as u16; src += 1; let i1 = input[src] as u16; src += 1; let i2 = input[src] as u16; src += 1; let o0 = i0 << 4 | i1 >> 4; let o1 = (i1 & 0xf) << 8 | i2; out_data.push(o0); out_data.push(o1); written += 2; } src += pad; } Ok(written) } /// Unpack 12-bits into 16-bits values LittleEndia /// For performance `out_data` should have reserved size /// Return the number of elements written. pub(crate) fn unpack_le12to16( input: &[u8], out_data: &mut Vec, compression: tiff::Compression, ) -> Result { let pad = if compression == tiff::Compression::Olympus { 1_usize } else { 0_usize }; let n = input.len() / (15 + pad); let rest = input.len() % (15 + pad); let mut src = 0_usize; // index in source if pad != 0 && (input.len() % 16) != 0 { log::error!("le12to16 incorrect padding for {:?}.", compression); return Err(Error::Decompression(format!( "le12to16 incorrect padding for {compression:?}.", ))); } if (rest % 3) != 0 { log::error!("le12to16 incorrect rest for {:?}.", compression); return Err(Error::Decompression(format!( "le12to16 incorrect rest for {compression:?}.", ))); } let mut written = 0_usize; for i in 0..=n { let m = if i == n { rest / 3 } else { 5 }; // XXX check overflow for _ in 0..m { let b1 = input[src] as u16; src += 1; let b2 = input[src] as u16; src += 1; let b3 = input[src] as u16; src += 1; out_data.push(((b2 & 0xf) << 8) | b1); out_data.push((b3 << 4) | (b2 >> 4)); written += 2; } src += pad; } Ok(written) } /// Unpack data at `offset` into a 16-bits buffer. pub(crate) fn unpack( container: &dyn RawContainer, width: u32, height: u32, bpc: u16, compression: tiff::Compression, offset: u64, byte_len: usize, ) -> Result> { let mut view = container.borrow_view_mut(); view.seek(SeekFrom::Start(offset))?; unpack_from_reader(&mut *view, width, height, bpc, compression, byte_len) } /// Unpack from a reader into a 16-bits buffer. pub(crate) fn unpack_from_reader( reader: &mut dyn Read, width: u32, height: u32, bpc: u16, compression: tiff::Compression, byte_len: usize, ) -> Result> { log::debug!( "Unpack {} bytes {} x {} - compression {:?}", byte_len, width, height, compression ); let block_size: usize = match bpc { 10 => (width / 4 * 5) as usize, 12 => { if compression == tiff::Compression::NikonPack || compression == tiff::Compression::Olympus { ((width / 2 * 3) + width / 10) as usize } else { (width / 2 * 3) as usize } } 14 => (width / 4 * 7) as usize, _ => { log::warn!("Invalid BPC {}", bpc); return Err(Error::InvalidFormat); } }; log::debug!("Block size = {}", block_size); let mut block = vec![0; block_size]; let out_size = width as usize * height as usize; let mut out_data = Vec::with_capacity(out_size); let mut fetched = 0_usize; let mut written = 0_usize; let byte_len = std::cmp::min(byte_len, block_size * height as usize); while fetched < byte_len { reader.read_exact(block.as_mut_slice())?; fetched += block.len(); match bpc { n @ 10 | n @ 14 => written += unpack_bento16(&block, n, width as usize, &mut out_data)?, 12 => { written += match compression { tiff::Compression::NikonPack | tiff::Compression::PentaxPack | tiff::Compression::None => { unpack_be12to16(&block, &mut out_data, compression)? } tiff::Compression::Olympus => { unpack_le12to16(&block, &mut out_data, compression)? } _ => unreachable!(), } } _ => unreachable!(), } } log::debug!("Unpacked {} pixels", written); Ok(out_data) } #[cfg(test)] mod test { use super::unpack_be12to16; use super::unpack_bento16; use crate::tiff; #[test] fn test_unpack() { let packed: [u8; 32] = [ 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0x00, ]; let mut unpacked: Vec = Vec::with_capacity(20); let result = unpack_be12to16( packed.as_slice(), &mut unpacked, tiff::Compression::NikonPack, ); assert!(matches!(result, Ok(20))); for i in 0..2 { assert_eq!(unpacked[10 * i], 0x0123); assert_eq!(unpacked[10 * i + 1], 0x0456); assert_eq!(unpacked[10 * i + 2], 0x0789); assert_eq!(unpacked[10 * i + 3], 0x00ab); assert_eq!(unpacked[10 * i + 4], 0x0cde); assert_eq!(unpacked[10 * i + 5], 0x0f12); assert_eq!(unpacked[10 * i + 6], 0x0345); assert_eq!(unpacked[10 * i + 7], 0x0678); assert_eq!(unpacked[10 * i + 8], 0x090a); assert_eq!(unpacked[10 * i + 9], 0x0bcd); } } #[test] fn test_unpack2() { let packed: [u8; 3] = [0x12, 0x34, 0x56]; let mut unpacked: Vec = Vec::with_capacity(2); let result = unpack_be12to16(packed.as_slice(), &mut unpacked, tiff::Compression::None); assert!(matches!(result, Ok(2))); assert_eq!(unpacked[0], 0x0123); assert_eq!(unpacked[1], 0x0456); } #[test] fn test_unpack14() { let buf: [u8; 7] = [ 0b1111_1111, 0b1111_1100, 0b0000_0000, 0b0000_1111, 0b1111_1111, 0b1100_0000, 0b0000_0000, ]; let mut unpacked: Vec = Vec::with_capacity(4); let result = unpack_bento16(buf.as_slice(), 14, 4, &mut unpacked); assert!(matches!(result, Ok(4))); assert_eq!(unpacked[0], 0b0011_1111_1111_1111); assert_eq!(unpacked[1], 0b0000_0000_0000_0000); assert_eq!(unpacked[2], 0b0011_1111_1111_1111); assert_eq!(unpacked[3], 0b0000_0000_0000_0000); } }