use std::num::NonZeroU32;
use crate::{util, Dithering, EncodeOptions, Format, ImageView, Offset, Size};
pub struct SplitView<'a> {
image: ImageView<'a>,
len: u32,
fragment_height: Option<NonZeroU32>,
}
impl<'a> SplitView<'a> {
pub fn new_single(image: ImageView<'a>) -> Self {
Self {
image,
len: 1,
fragment_height: None,
}
}
pub fn new(image: ImageView<'a>, format: Format, options: &EncodeOptions) -> Self {
if let Some(fragment_height) = get_fragment_height(image.size(), format, options) {
let len = util::div_ceil(image.height(), fragment_height.get());
Self {
image,
len,
fragment_height: Some(fragment_height),
}
} else {
Self::new_single(image)
}
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u32 {
self.len
}
pub fn get(&self, index: u32) -> Option<ImageView<'a>> {
if index >= self.len {
return None;
}
if let Some(full_fragment_height) = self.fragment_height {
let start_y = index * full_fragment_height.get();
let end_y = start_y
.saturating_add(full_fragment_height.get())
.min(self.image.height());
debug_assert!(start_y < self.image.height());
let fragment_height = end_y - start_y;
Some(self.image.cropped(
Offset::new(0, start_y),
Size::new(self.image.width(), fragment_height),
))
} else {
Some(self.image)
}
}
pub fn single(&self) -> Option<ImageView<'a>> {
if self.len() == 1 {
Some(self.image)
} else {
None
}
}
}
fn get_fragment_height(size: Size, format: Format, options: &EncodeOptions) -> Option<NonZeroU32> {
if size.is_empty() {
return None;
}
let support = format.encoding_support()?;
let split_height = support.split_height()?;
if !support.local_dithering()
&& options.dithering.intersect(support.dithering()) != Dithering::None
{
return None;
}
let fragment_pixels = support.fragment_size.get_preferred(options.quality).max(1);
if fragment_pixels >= size.pixels() {
return None;
}
let split_height_64 = split_height.get() as u64;
let fragment_height_or_zero =
u32::try_from((fragment_pixels / size.width as u64) / split_height_64 * split_height_64)
.ok()?;
let fragment_height = NonZeroU32::new(fragment_height_or_zero).unwrap_or(split_height.into());
Some(fragment_height)
}