mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-13 01:25:24 +00:00
Merge main into #2200
This commit is contained in:
@@ -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");
|
||||
}
|
||||
@@ -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
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user