use crate::export_quality::types::{ExportFormatQualityScore, ExportQualityCheck, check};
use crate::forge::ExportFormat;
use image::GenericImageView;
const DOCUMENT_MIN_WIDTH: u32 = 640;
const DOCUMENT_MIN_HEIGHT: u32 = 480;
const DOCUMENT_MIN_AREA: u32 = DOCUMENT_MIN_WIDTH * DOCUMENT_MIN_HEIGHT;
const NON_EMPTY_SCORE_WEIGHT: u16 = 20;
const SIGNATURE_SCORE_WEIGHT: u16 = 20;
const DECODE_SCORE_WEIGHT: u16 = 25;
const SCALE_SCORE_WEIGHT: u16 = 25;
const AREA_SCORE_WEIGHT: u16 = 10;
pub(crate) struct ImageQualityScore;
impl ImageQualityScore {
pub(crate) fn score(
format: ExportFormat,
bytes: &[u8],
decoded: Result<(u32, u32), image::ImageError>,
signature: &[u8],
) -> ExportFormatQualityScore {
let label = format!("{format:?}").to_lowercase();
ExportFormatQualityScore::new(format, Self::checks(&label, bytes, &decoded, signature))
}
pub(crate) fn decode_dimensions(bytes: &[u8]) -> Result<(u32, u32), image::ImageError> {
image::load_from_memory(bytes).map(|image| image.dimensions())
}
fn checks(
label: &str,
bytes: &[u8],
decoded: &Result<(u32, u32), image::ImageError>,
signature: &[u8],
) -> Vec<ExportQualityCheck> {
vec![
Self::non_empty_check(label, bytes),
Self::signature_check(label, bytes, signature),
Self::decode_check(label, decoded),
Self::scale_check(label, decoded),
Self::area_check(label, decoded),
]
}
fn non_empty_check(label: &str, bytes: &[u8]) -> ExportQualityCheck {
check(
&format!("{label} is non-empty"),
!bytes.is_empty(),
true,
NON_EMPTY_SCORE_WEIGHT,
)
}
fn signature_check(label: &str, bytes: &[u8], signature: &[u8]) -> ExportQualityCheck {
check(
&format!("{label} has signature"),
bytes.starts_with(signature),
true,
SIGNATURE_SCORE_WEIGHT,
)
}
fn decode_check(
label: &str,
decoded: &Result<(u32, u32), image::ImageError>,
) -> ExportQualityCheck {
check(
&format!("{label} decodes"),
decoded.is_ok(),
true,
DECODE_SCORE_WEIGHT,
)
}
fn scale_check(
label: &str,
decoded: &Result<(u32, u32), image::ImageError>,
) -> ExportQualityCheck {
check(
&format!("{label} dimensions are document scale"),
document_scale(decoded),
true,
SCALE_SCORE_WEIGHT,
)
}
fn area_check(
label: &str,
decoded: &Result<(u32, u32), image::ImageError>,
) -> ExportQualityCheck {
check(
&format!("{label} is not visually blank"),
has_area(decoded),
true,
AREA_SCORE_WEIGHT,
)
}
}
fn document_scale(decoded: &Result<(u32, u32), image::ImageError>) -> bool {
decoded
.as_ref()
.is_ok_and(|(width, height)| *width >= DOCUMENT_MIN_WIDTH && *height >= DOCUMENT_MIN_HEIGHT)
}
fn has_area(decoded: &Result<(u32, u32), image::ImageError>) -> bool {
decoded
.as_ref()
.is_ok_and(|(width, height)| width.saturating_mul(*height) > DOCUMENT_MIN_AREA)
}