use crate::device_profiles;
use crate::error::{DecodeError, DecodedImage};
use crate::photodb::parser::{can_open_photodb, try_parse_photodb};
use crate::pipeline::decode_ithmb;
use crate::pipeline::decode_with_profile;
use crate::pipeline::profile_loader::{fallback_jpeg_profile, get_db};
use crate::profile::Encoding;
use std::sync::atomic::AtomicBool;
pub(crate) const MAX_RAW_FILE_SIZE: usize = 8 * 1024 * 1024;
#[allow(clippy::cast_sign_loss)]
pub fn open_ithmb(
src: &[u8],
canceled: &AtomicBool,
device_name: Option<&str>,
) -> Result<Vec<DecodedImage>, DecodeError> {
if src.len() > MAX_RAW_FILE_SIZE {
return Err(DecodeError::FileTooLarge {
size: src.len(),
limit: MAX_RAW_FILE_SIZE,
});
}
if can_open_photodb(src) {
let mut entries = Vec::new();
try_parse_photodb(src, &mut entries)?;
let allowed_formats: Option<Vec<i32>> = device_name.map(|name| {
device_profiles::find_device(name)
.map(|dp| dp.formats.iter().map(|f| f.format_id).collect())
.unwrap_or_default()
});
let db = get_db();
let mut results = Vec::with_capacity(entries.len());
for entry in &entries {
if let Some(ref allowed) = allowed_formats
&& !allowed.contains(&entry.format_id)
{
continue;
}
if entry.data.is_empty() {
continue;
}
if let Some(profile) = db.get(entry.format_id) {
let prefixed = if profile.encoding == Encoding::Jpeg {
entry.data.clone()
} else {
let prefix_bytes = (profile.prefix as u32).to_be_bytes();
let mut with_prefix = Vec::with_capacity(4 + entry.data.len());
with_prefix.extend_from_slice(&prefix_bytes);
with_prefix.extend_from_slice(&entry.data);
with_prefix
};
let img = decode_with_profile(&prefixed, profile, canceled)?;
results.push(img);
} else if entry.data.len() >= 2 && entry.data[0] == 0xFF && entry.data[1] == 0xD8 {
let mut profile = fallback_jpeg_profile();
profile.width = entry.width;
profile.height = entry.height;
let img = decode_with_profile(&entry.data, &profile, canceled)?;
results.push(img);
}
}
Ok(results)
} else {
Ok(decode_ithmb(src, canceled).map(|img| vec![img])?)
}
}