openpencil/crates/op-host-native/tests/raster_composition.rs
Kayshen-X 1e0b3cab7a refactor(rust): rename native + desktop hosts to op-host-* crates
Phase 7.3 strangler reorg — rename the native widget host and the
desktop runner crates to the op- prefix. The desktop+native merge was
declined: keeping the library / binary split preserves the mobile-
checkable op-host-native lib (cargo check -p op-host-native on iOS /
Android, relied on by check-jian-boundaries.sh + the CI mobile job),
which a folded-in winit binary would break. A clean separate rename is
purely mechanical and the brief permits it.

- openpencil-shell-native -> op-host-native (lib op_host_native)
- openpencil-desktop -> op-host-desktop crate; the shipped executable
  keeps the stable openpencil-desktop [[bin]] name so release
  artifacts + external CLI integrations are unaffected
- every openpencil_shell_core:: path -> op_editor_ui::
- every openpencil_shell_native:: path -> op_host_native::
- doc-comment / manual-smoke note refs updated
2026-05-16 23:49:58 +08:00

95 lines
3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Spec v19 acceptance #2 (chrome region) / plan v7 Task 2 Step 16d.
//!
//! Round 5 BLOCK-R5-2 fix: this test covers **chrome only** on a raster
//! surface. The chrome+stub composition lives in
//! `gpu_chrome_stub_composition.rs` because `CanvasViewportStub` needs
//! a real `glow::Context` to mutate GL state, which only the GPU smoke
//! provider has. Here we just prove that `NativeBackend` writes the
//! pixels widgets ask for at the right coordinates — independent of
//! GL state.
use op_editor_ui::{Color, Point2D, Rect};
use op_host_native::NativeBackend;
fn pixel_at(pixels: &[u8], stride: usize, x: i32, y: i32) -> [u8; 4] {
let offset = (y as usize) * stride + (x as usize) * 4;
[
pixels[offset],
pixels[offset + 1],
pixels[offset + 2],
pixels[offset + 3],
]
}
#[test]
fn chrome_rects_paint_at_expected_coordinates() {
// 500 × 400 raster surface. RGBA8888 / N32 premul matches what
// `surfaces::wrap_backend_render_target` would set for a GL surface
// — the per-component byte order is identical on little-endian.
let mut surface =
skia_safe::surfaces::raster_n32_premul((500, 400)).expect("raster_n32_premul allocated");
// Clear to white so chrome paints stand out and "background"
// assertions know what to expect.
surface.canvas().clear(skia_safe::Color::WHITE);
let mut backend = NativeBackend::with_dpi(1.0);
let canvas = surface.canvas();
// Chrome rect 1: red, top-left chrome.
backend.fill_rect(
canvas,
Rect {
origin: Point2D::new(50.0, 50.0),
size: Point2D::new(100.0, 100.0),
},
Color::RED,
);
// Chrome rect 2: black, separate region — verifies multi-fill
// isn't polluted by the first call.
backend.fill_rect(
canvas,
Rect {
origin: Point2D::new(250.0, 50.0),
size: Point2D::new(50.0, 50.0),
},
Color::BLACK,
);
// Read pixels back via `Surface::read_pixels`; raster surfaces
// expose CPU memory directly so the bytes match what we'd see on
// a GL surface for the same draw.
let stride = 500 * 4;
let mut pixels = vec![0u8; stride * 400];
let info = skia_safe::ImageInfo::new(
(500, 400),
skia_safe::ColorType::RGBA8888,
skia_safe::AlphaType::Premul,
None,
);
let read_ok = surface.read_pixels(&info, &mut pixels, stride, (0, 0));
assert!(read_ok, "raster Surface::read_pixels must succeed");
// (a) red chrome interior — middle of the (50,50)-(150,150) rect.
assert_eq!(
pixel_at(&pixels, stride, 75, 75),
[255, 0, 0, 255],
"red chrome"
);
// (b) black chrome interior — middle of the (250,50)-(300,100) rect.
assert_eq!(
pixel_at(&pixels, stride, 275, 75),
[0, 0, 0, 255],
"black chrome"
);
// (c) untouched background pixel — well outside both rects.
assert_eq!(
pixel_at(&pixels, stride, 400, 300),
[255, 255, 255, 255],
"background unchanged"
);
}