///|
/// Scale planning helpers for screen recordings and sticker exports.
pub(all) struct ScalePlan {
source_width : Int
source_height : Int
target_width : Int
target_height : Int
scale_per_mille : Int
crop : Bool
} derive(Debug)
///|
pub fn ScalePlan::source(self : ScalePlan) -> (Int, Int) {
(self.source_width, self.source_height)
}
///|
pub fn ScalePlan::target(self : ScalePlan) -> (Int, Int) {
(self.target_width, self.target_height)
}
///|
pub fn ScalePlan::scale(self : ScalePlan) -> Int {
self.scale_per_mille
}
///|
pub fn ScalePlan::crop(self : ScalePlan) -> Bool {
self.crop
}
///|
/// Choose a contain or cover plan with integer aspect comparison.
pub fn make_scale_plan(
source_width : Int,
source_height : Int,
target_width : Int,
target_height : Int,
crop : Bool,
) -> ScalePlan {
let sw = if source_width < 1 { 1 } else { source_width }
let sh = if source_height < 1 { 1 } else { source_height }
let tw = if target_width < 1 { 1 } else { target_width }
let th = if target_height < 1 { 1 } else { target_height }
let horizontal = tw * 1000 / sw
let vertical = th * 1000 / sh
let scale = if crop {
if horizontal > vertical {
horizontal
} else {
vertical
}
} else if horizontal < vertical {
horizontal
} else {
vertical
}
ScalePlan::{
source_width: sw,
source_height: sh,
target_width: tw,
target_height: th,
scale_per_mille: scale,
crop,
}
}
///|
/// Execute a scale plan with stable nearest-neighbor behavior.
pub fn Canvas::apply_scale_plan(self : Canvas, plan : ScalePlan) -> Canvas {
let scaled = self.resize_nearest(
width=self.width * plan.scale_per_mille / 1000,
height=self.height * plan.scale_per_mille / 1000,
)
if plan.crop {
scaled.crop(
x=(scaled.width - plan.target_width) / 2,
y=(scaled.height - plan.target_height) / 2,
width=plan.target_width,
height=plan.target_height,
)
} else {
contain_layout(
scaled,
plan.target_width,
plan.target_height,
rgba(0, 0, 0, 0),
)
}
}
///|
/// Apply one scale plan to every frame and reset offsets to the new screen.
pub fn Animation::apply_scale_plan(
self : Animation,
plan : ScalePlan,
) -> Animation {
let result = Animation::new(
width=plan.target_width,
height=plan.target_height,
)
.with_loop_count(loop_count=self.loop_count)
.with_background(background=self.background)
for frame in self.frames {
ignore(
result.add_frame(
frame
.with_canvas(frame.canvas().apply_scale_plan(plan))
.with_offset(left=0, top=0),
),
)
}
result
}