use crate::color::{CanonicalColor, ColorLowering};
use crate::normalization::{NormalizedElement, NormalizedElementKind};
use crate::ColorMode;
use super::super::geometry::GeneratedGeometry;
use super::super::text::{FallbackFont, FrameName, FRAME_NAME_BOX_HEIGHT, FRAME_NAME_OFFSET_Y};
use super::{
label_error, FrameMembership, ResolveContext, ResolveSceneError, ResolvedFrame,
ResolvedFrameName, ResolvedFrameOutline, SemanticFrameSource,
};
const FRAME_NAME_COLOR_LIGHT: CanonicalColor = CanonicalColor::from_rgb8(0x99, 0x99, 0x99);
const FRAME_NAME_COLOR_DARK: CanonicalColor = CanonicalColor::from_rgb8(0x7a, 0x7a, 0x7a);
pub(super) fn resolve_frame_names(
outlines: &[(NormalizedElement, Option<GeneratedGeometry>)],
fallback_font: Option<&FallbackFont>,
) -> Result<Vec<(usize, FrameName)>, ResolveSceneError> {
let mut frame_names = Vec::new();
for (source, _) in outlines {
let (NormalizedElementKind::Frame(frame) | NormalizedElementKind::MagicFrame(frame)) =
&source.kind
else {
continue;
};
let title = frame.name.as_deref().unwrap_or({
if matches!(source.kind, NormalizedElementKind::MagicFrame(_)) {
"AI Frame"
} else {
"Frame"
}
});
let font = fallback_font.ok_or(ResolveSceneError::InvalidFallbackFont)?;
let name = font
.resolve_frame_name(title, source.width)
.map_err(label_error)?;
frame_names.try_reserve(1)?;
frame_names.push((source.source.source_index(), name));
}
Ok(frame_names)
}
pub(super) fn semantic_frame_sources(
outlines: &[(NormalizedElement, Option<GeneratedGeometry>)],
) -> Result<Vec<SemanticFrameSource>, ResolveSceneError> {
let mut semantic_frames = Vec::new();
semantic_frames.try_reserve(outlines.len())?;
for (source, _) in outlines {
if matches!(
source.kind,
NormalizedElementKind::Frame(_) | NormalizedElementKind::MagicFrame(_)
) {
semantic_frames.push(SemanticFrameSource {
source_index: source.source.source_index(),
x: source.x,
y: source.y,
width: source.width,
height: source.height,
angle: source.angle,
opacity: source.opacity / 100.0,
});
}
}
Ok(semantic_frames)
}
fn frame_name_color(theme: ColorMode, lowering: ColorLowering) -> CanonicalColor {
match theme {
ColorMode::Light => FRAME_NAME_COLOR_LIGHT,
ColorMode::Dark => FRAME_NAME_COLOR_DARK,
}
.lowered(lowering)
}
pub(super) fn resolve_frame(
source: NormalizedElement,
context: &ResolveContext<'_>,
membership: FrameMembership,
frame_names: &mut Vec<(usize, FrameName)>,
) -> Result<Option<ResolvedFrame>, ResolveSceneError> {
if !(context.frame_rendering.enabled && context.frame_rendering.emit_frame_elements) {
return Ok(None);
}
let name = frame_names
.iter()
.position(|(index, _)| *index == source.source.source_index())
.map(|position| frame_names.swap_remove(position).1)
.map(|name| ResolvedFrameName {
transform: context.transform(
source.x,
source.y - FRAME_NAME_OFFSET_Y - FRAME_NAME_BOX_HEIGHT,
0.0,
name.width / 2.0,
FRAME_NAME_BOX_HEIGHT / 2.0,
),
color: frame_name_color(
context.request.color_mode(),
context.request.color_lowering(),
),
label: name.label,
});
let outline = context
.frame_rendering
.outline
.then(|| {
ResolvedFrameOutline::new(
source.width,
source.height,
matches!(source.kind, NormalizedElementKind::MagicFrame(_)),
context.request.color_mode(),
context.request.color_lowering(),
)
})
.transpose()?;
let transform = context.box_transform(&source);
Ok(Some(ResolvedFrame {
source_order: source.source_order,
source: source.source,
name,
outline,
frame_clips: membership.clips,
transform,
opacity: source.opacity / 100.0 * membership.opacity,
link: source.link,
width: source.width,
height: source.height,
}))
}