Merge main into #2200

This commit is contained in:
bflatastic
2026-11-12 14:04:50 +07:00
4 changed files with 54 additions and 4 deletions
+7 -2
View File
@@ -360,8 +360,13 @@ impl Session {
pub(super) fn eyedrop(&mut self, ev: &PointerEvent) -> Result<()> {
let ds = self.ds()?;
let doc = ds.doc.clone();
let view = figcraft_render::View { origin: (ev.x, ev.y), scale: ev.zoom.max(0.01), width: 1, height: 1 };
let Some(pix) = figcraft_render::render_view(&doc, ds.page, &view, &Default::default()) else { return Ok(()) };
// One screen pixel centred on the pointer (its top left half a pixel up and to the left).
let scale = ev.zoom.max(0.01);
let view = figcraft_render::View { origin: (ev.x - 0.5 / scale, ev.y - 0.5 / scale), scale, width: 1, height: 1 };
// As the canvas draws it: an image magnified past 2x shows its pixels, so the colour picked
// is the pixel under the pointer, not a blend with its neighbours (#2189).
let opts = figcraft_render::Options { magnified_image_pixels: true, ..Default::default() };
let Some(pix) = figcraft_render::render_view(&doc, ds.page, &view, &opts) else { return Ok(()) };
let rgba = figcraft_render::to_rgba8(&pix);
let c = figcraft_color::Color::from_rgba8(
rgba.first().copied().unwrap_or(0),
@@ -0,0 +1,42 @@
//! #2189: the eyedropper picks the colour the canvas shows under the pointer. A magnified image
//! draws its pixels on the canvas (`magnified_image_pixels`), so the picked colour is that pixel's,
//! not a blend with its neighbours.
use base64::Engine as _;
use serde_json::json;
use crate::Session;
use crate::tools::{PointerEvent, PointerKind};
/// A 9 x 9 image of `back` with a 1 px vertical line of `line` in column 4 (a thin border in a
/// screenshot, as in the report).
fn line_png(back: [u8; 3], line: [u8; 3]) -> Vec<u8> {
let mut p = figcraft_render::Pixmap::new(9, 9).unwrap();
for (i, px) in p.data_mut().chunks_exact_mut(4).enumerate() {
let c = if i % 9 == 4 { line } else { back };
px.copy_from_slice(&[c[0], c[1], c[2], 255]);
}
figcraft_render::encode_png(&p).unwrap()
}
/// The colour the eyedropper picks at page point (x, y) at `zoom`.
fn picked(png: &[u8], x: f64, y: f64, zoom: f64) -> String {
let mut s = Session::new();
let data = base64::engine::general_purpose::STANDARD.encode(png);
s.execute("image.place", &json!({"data": data, "x": 0, "y": 0})).unwrap();
s.execute("edit.pickColor", &json!({"apply": false})).unwrap();
s.pointer(&PointerEvent::at(PointerKind::Down, x, y, zoom)).unwrap();
s.tool.picked.map(|c| c.to_hex()).unwrap_or_default()
}
#[test]
fn the_eyedropper_picks_a_magnified_images_pixel_not_a_blend_with_its_neighbours() {
// The reporter's colours: a #3D444D border, 1 px wide, beside a darker background.
let png = line_png([0x15, 0x1B, 0x23], [0x3D, 0x44, 0x4D]);
for zoom in [2.0, 8.0, 20.0] {
assert_eq!(picked(&png, 4.5, 4.5, zoom), "3D444D", "on the line at {zoom}x");
assert_eq!(picked(&png, 2.5, 4.5, zoom), "151B23", "beside it at {zoom}x");
}
// At 1:1 the canvas pixel is the image's pixel too.
assert_eq!(picked(&png, 4.5, 4.5, 1.0), "3D444D", "on the line at 1x");
}
+2
View File
@@ -327,6 +327,8 @@ mod extended_chain_tests;
#[cfg(test)]
mod eyedropper_create_tests;
#[cfg(test)]
mod eyedropper_image_tests;
#[cfg(test)]
mod facilitate_tests;
#[cfg(test)]
mod fig_export_assets_tests;
+3 -2
View File
File diff suppressed because one or more lines are too long