Skip to content

Commit 1449792

Browse files
zachelnetDeepSeek V4
andcommitted
perf: throttle rotation slider, add 90/180/270 fast path
- Range slider now buffers via rotationDraft and commits only on mouseUp, avoiding per-pixel applyOp/queueAutoRender calls during drag. - rotate_sprite_expand_top_left detects exact 90° multiples and uses direct integer pixel rearrangement instead of bilinear resampling, avoiding a full per-pixel float pass + allocation for common angles. Co-authored-by: DeepSeek V4 <deepseek@v4.ai>
1 parent f03f41c commit 1449792

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

crates/koharu-app/src/renderer.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,45 @@ fn rotate_sprite_expand_top_left(src: &RgbaImage, angle_rad: f32) -> (RgbaImage,
10451045
return (src.clone(), 0.0, 0.0);
10461046
}
10471047

1048+
// Fast path: exact 90° multiples — direct integer pixel rearrangement,
1049+
// no resampling. The layout matches the bilinear path exactly.
1050+
let deg = angle_rad.to_degrees();
1051+
let remainder = deg % 90.0;
1052+
if remainder.abs() < 0.001 || (90.0 - remainder.abs()).abs() < 0.001 {
1053+
let normalized = ((deg % 360.0) + 360.0) % 360.0;
1054+
return match normalized.round() as i32 {
1055+
0 => (src.clone(), 0.0, 0.0),
1056+
90 => {
1057+
let mut dst = RgbaImage::new(src_h, src_w);
1058+
for y in 0..src_h {
1059+
for x in 0..src_w {
1060+
dst.put_pixel(src_h - 1 - y, x, *src.get_pixel(x, y));
1061+
}
1062+
}
1063+
(dst, -(src_h as f32), 0.0)
1064+
}
1065+
180 => {
1066+
let mut dst = RgbaImage::new(src_w, src_h);
1067+
for y in 0..src_h {
1068+
for x in 0..src_w {
1069+
dst.put_pixel(src_w - 1 - x, src_h - 1 - y, *src.get_pixel(x, y));
1070+
}
1071+
}
1072+
(dst, -(src_w as f32), -(src_h as f32))
1073+
}
1074+
270 => {
1075+
let mut dst = RgbaImage::new(src_h, src_w);
1076+
for y in 0..src_h {
1077+
for x in 0..src_w {
1078+
dst.put_pixel(y, src_w - 1 - x, *src.get_pixel(x, y));
1079+
}
1080+
}
1081+
(dst, 0.0, -(src_w as f32))
1082+
}
1083+
_ => unreachable!(),
1084+
};
1085+
}
1086+
10481087
let cos = angle_rad.cos();
10491088
let sin = angle_rad.sin();
10501089
let corners = [

ui/components/panels/RenderControlsPanel.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -829,9 +829,14 @@ export function RenderControlsPanel() {
829829
max={String(MAX_ROTATION_DEG)}
830830
step='1'
831831
disabled={!selectedNode}
832-
value={selectedNode?.transform.rotationDeg ?? 0}
832+
value={rotationDraft ?? String(selectedNode?.transform.rotationDeg ?? 0)}
833833
onChange={(event) => {
834-
const parsed = Number.parseFloat(event.target.value)
834+
setRotationDraft(event.target.value)
835+
}}
836+
onMouseUp={() => {
837+
if (rotationDraft == null) return
838+
const parsed = Number.parseFloat(rotationDraft)
839+
setRotationDraft(null)
835840
if (!Number.isFinite(parsed)) return
836841
void updateSelectedRotation(parsed)
837842
}}

0 commit comments

Comments
 (0)