use hcie_engine_api::Engine; fn count_opaque_pixels(pixels: &[u8]) -> usize { pixels.iter().skip(3).step_by(4).filter(|&&a| a > 0).count() } fn has_non_black_pixel(pixels: &[u8]) -> bool { pixels.chunks_exact(4).any(|px| px[0] > 0 || px[1] > 0 || px[2] > 0 || px[3] > 0) } #[test] fn test_visibility_toggle_bug_repro() { let mut engine = Engine::new(100, 100); // Fill background layer (layer 0) with opaque white let bg_id = engine.active_layer_id(); engine.draw_filled_rect_rgba(0, 0, 100, 100, [255, 255, 255, 255]); log::info!("=== AFTER FILL BG: pixels should be white ==="); let p = engine.get_composite_pixels(); log::info!(" composite opaque pixels: {}/{}", count_opaque_pixels(&p), 100*100); assert_eq!(count_opaque_pixels(&p), 100*100, "background should be fully opaque"); // Add a new layer on top let top_id = engine.add_layer("Top Layer"); log::info!("=== AFTER ADD TOP LAYER: id={} ===", top_id); let p = engine.get_composite_pixels(); log::info!(" composite opaque pixels: {}/{} (expected 10000)", count_opaque_pixels(&p), 100*100); // After adding layer, composite should still show the background (10000 opaque) // Draw something on the top layer engine.draw_filled_rect_rgba(20, 20, 80, 80, [0, 0, 0, 255]); log::info!("=== AFTER DRAW ON TOP: black square 60x60 at (20,20) ==="); let p = engine.get_composite_pixels(); log::info!(" composite opaque pixels: {}/{} (expected 10000)", count_opaque_pixels(&p), 100*100); assert_eq!(count_opaque_pixels(&p), 100*100, "all pixels should still be opaque"); // Now hide the BACKGROUND layer log::info!("=== HIDE BACKGROUND (id={}) ===", bg_id); engine.set_layer_visible(bg_id, false); let p = engine.get_composite_pixels(); log::info!(" composite opaque pixels: {}/{} (expected 3600 = 60*60 black square)", count_opaque_pixels(&p), 100*100); log::info!(" has non-black: {}", has_non_black_pixel(&p)); // After hiding background, only the black square (3600 px) should be visible assert!(count_opaque_pixels(&p) > 0, "at least the drawn square should be visible"); assert!(count_opaque_pixels(&p) <= 3600 + 100, "only drawn square should be visible, got {}", count_opaque_pixels(&p)); // Show background again log::info!("=== SHOW BACKGROUND (id={}) ===", bg_id); engine.set_layer_visible(bg_id, true); let p = engine.get_composite_pixels(); log::info!(" composite opaque pixels: {}/{} (expected 10000)", count_opaque_pixels(&p), 100*100); assert_eq!(count_opaque_pixels(&p), 100*100, "all pixels should be opaque again"); } #[test] fn test_visibility_toggle_with_stroke() { let mut engine = Engine::new(100, 100); let bg_id = engine.active_layer_id(); engine.draw_filled_rect_rgba(0, 0, 100, 100, [255, 255, 255, 255]); let top_id = engine.add_layer("Top Layer"); // Hide background log::info!("=== HIDE BG, THEN STROKE ON TOP ==="); engine.set_layer_visible(bg_id, false); // Begin a stroke on the top layer engine.begin_stroke(top_id, 50.0, 50.0); engine.stroke_to(top_id, 55.0, 55.0, 1.0); engine.stroke_to(top_id, 60.0, 60.0, 1.0); engine.end_stroke(top_id); let p = engine.get_composite_pixels(); let opaque = count_opaque_pixels(&p); log::info!(" After stroke with bg hidden: opaque pixels: {}/{}", opaque, 100*100); log::info!(" has non-black: {}", has_non_black_pixel(&p)); // The stroke should be visible assert!(opaque > 0, "stroke should be visible even with bg hidden"); } #[test] fn test_visibility_toggle_during_stroke() { let mut engine = Engine::new(100, 100); let bg_id = engine.active_layer_id(); engine.draw_filled_rect_rgba(0, 0, 100, 100, [255, 255, 255, 255]); let top_id = engine.add_layer("Top Layer"); // Begin a stroke on the top layer log::info!("=== STROKE THEN HIDE BG DURING STROKE ==="); engine.begin_stroke(top_id, 50.0, 50.0); engine.stroke_to(top_id, 55.0, 55.0, 1.0); // Hide background mid-stroke engine.set_layer_visible(bg_id, false); engine.stroke_to(top_id, 60.0, 60.0, 1.0); engine.stroke_to(top_id, 65.0, 65.0, 1.0); engine.end_stroke(top_id); let p = engine.get_composite_pixels(); let opaque = count_opaque_pixels(&p); log::info!(" After stroke with bg hidden mid-stroke: opaque pixels: {}/{}", opaque, 100*100); assert!(opaque > 0, "stroke should be visible even after hiding bg mid-stroke"); }