Fix the Eyedropper tool on web with Vello and on desktop with SVG (#3886)
This commit is contained in:
parent
116a4106c4
commit
81d0b8b8d4
|
|
@ -1191,7 +1191,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||||
Ok(message) => responses.add_front(message),
|
Ok(message) => responses.add_front(message),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(target_family = "wasm"))]
|
|
||||||
PortfolioMessage::SubmitEyedropperPreviewRender => {
|
PortfolioMessage::SubmitEyedropperPreviewRender => {
|
||||||
use crate::consts::EYEDROPPER_PREVIEW_AREA_RESOLUTION;
|
use crate::consts::EYEDROPPER_PREVIEW_AREA_RESOLUTION;
|
||||||
|
|
||||||
|
|
@ -1223,10 +1222,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||||
Ok(message) => responses.add_front(message),
|
Ok(message) => responses.add_front(message),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
PortfolioMessage::SubmitEyedropperPreviewRender => {
|
|
||||||
// TODO: Currently for Wasm, this is implemented through SVG rendering but the Eyedropper tool doesn't work at all when Vello is enabled as the renderer
|
|
||||||
}
|
|
||||||
PortfolioMessage::ToggleFocusDocument => {
|
PortfolioMessage::ToggleFocusDocument => {
|
||||||
self.focus_document = !self.focus_document;
|
self.focus_document = !self.focus_document;
|
||||||
responses.add(MenuBarMessage::SendLayout);
|
responses.add(MenuBarMessage::SendLayout);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::tool_prelude::*;
|
use super::tool_prelude::*;
|
||||||
use crate::messages::frontend::utility_types::EyedropperPreviewImage;
|
use crate::messages::frontend::utility_types::EyedropperPreviewImage;
|
||||||
use crate::messages::tool::utility_types::DocumentToolData;
|
use crate::messages::tool::utility_types::DocumentToolData;
|
||||||
|
use graphene_std::vector::style::RenderMode;
|
||||||
|
|
||||||
#[derive(Default, ExtractField)]
|
#[derive(Default, ExtractField)]
|
||||||
pub struct EyedropperTool {
|
pub struct EyedropperTool {
|
||||||
|
|
@ -108,14 +109,19 @@ impl Fsm for EyedropperToolFsmState {
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||||
let ToolActionMessageContext {
|
let ToolActionMessageContext {
|
||||||
global_tool_data, input, viewport, ..
|
document,
|
||||||
|
global_tool_data,
|
||||||
|
input,
|
||||||
|
viewport,
|
||||||
|
..
|
||||||
} = tool_action_data;
|
} = tool_action_data;
|
||||||
|
let render_mode = document.render_mode;
|
||||||
|
|
||||||
let ToolMessage::Eyedropper(event) = event else { return self };
|
let ToolMessage::Eyedropper(event) = event else { return self };
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
// Ready -> Sampling
|
// Ready -> Sampling
|
||||||
(EyedropperToolFsmState::Ready, mouse_down) if matches!(mouse_down, EyedropperToolMessage::SamplePrimaryColorBegin | EyedropperToolMessage::SampleSecondaryColorBegin) => {
|
(EyedropperToolFsmState::Ready, mouse_down) if matches!(mouse_down, EyedropperToolMessage::SamplePrimaryColorBegin | EyedropperToolMessage::SampleSecondaryColorBegin) => {
|
||||||
update_cursor_preview(responses, tool_data, input, global_tool_data, None);
|
update_cursor_preview(responses, tool_data, input, global_tool_data, None, render_mode);
|
||||||
|
|
||||||
if mouse_down == EyedropperToolMessage::SamplePrimaryColorBegin {
|
if mouse_down == EyedropperToolMessage::SamplePrimaryColorBegin {
|
||||||
EyedropperToolFsmState::SamplingPrimary
|
EyedropperToolFsmState::SamplingPrimary
|
||||||
|
|
@ -127,7 +133,7 @@ impl Fsm for EyedropperToolFsmState {
|
||||||
(EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary, EyedropperToolMessage::PointerMove) => {
|
(EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary, EyedropperToolMessage::PointerMove) => {
|
||||||
let mouse_position = viewport.logical(input.mouse.position);
|
let mouse_position = viewport.logical(input.mouse.position);
|
||||||
if viewport.is_in_bounds(mouse_position + viewport.offset()) {
|
if viewport.is_in_bounds(mouse_position + viewport.offset()) {
|
||||||
update_cursor_preview(responses, tool_data, input, global_tool_data, None);
|
update_cursor_preview(responses, tool_data, input, global_tool_data, None, render_mode);
|
||||||
} else {
|
} else {
|
||||||
disable_cursor_preview(responses, tool_data);
|
disable_cursor_preview(responses, tool_data);
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +147,7 @@ impl Fsm for EyedropperToolFsmState {
|
||||||
EyedropperToolFsmState::SamplingSecondary => PrimarySecondary::Secondary,
|
EyedropperToolFsmState::SamplingSecondary => PrimarySecondary::Secondary,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
update_cursor_preview(responses, tool_data, input, global_tool_data, Some(set_color_choice));
|
update_cursor_preview(responses, tool_data, input, global_tool_data, Some(set_color_choice), render_mode);
|
||||||
disable_cursor_preview(responses, tool_data);
|
disable_cursor_preview(responses, tool_data);
|
||||||
|
|
||||||
EyedropperToolFsmState::Ready
|
EyedropperToolFsmState::Ready
|
||||||
|
|
@ -192,31 +198,29 @@ fn disable_cursor_preview(responses: &mut VecDeque<Message>, tool_data: &mut Eye
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_family = "wasm"))]
|
|
||||||
fn update_cursor_preview(
|
|
||||||
responses: &mut VecDeque<Message>,
|
|
||||||
tool_data: &mut EyedropperToolData,
|
|
||||||
_input: &InputPreprocessorMessageHandler,
|
|
||||||
_global_tool_data: &DocumentToolData,
|
|
||||||
set_color_choice: Option<PrimarySecondary>,
|
|
||||||
) {
|
|
||||||
tool_data.preview = true;
|
|
||||||
tool_data.color_choice = set_color_choice;
|
|
||||||
responses.add(PortfolioMessage::SubmitEyedropperPreviewRender);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
fn update_cursor_preview(
|
fn update_cursor_preview(
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
tool_data: &mut EyedropperToolData,
|
tool_data: &mut EyedropperToolData,
|
||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
global_tool_data: &DocumentToolData,
|
global_tool_data: &DocumentToolData,
|
||||||
set_color_choice: Option<PrimarySecondary>,
|
set_color_choice: Option<PrimarySecondary>,
|
||||||
|
render_mode: RenderMode,
|
||||||
) {
|
) {
|
||||||
tool_data.preview = true;
|
tool_data.preview = true;
|
||||||
tool_data.color_choice = set_color_choice.clone();
|
tool_data.color_choice = set_color_choice;
|
||||||
|
|
||||||
|
// On web, SVG Preview mode uses the frontend's SVG rasterization to sample pixels directly
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
if render_mode == RenderMode::SvgPreview {
|
||||||
update_cursor_preview_common(responses, None, input, global_tool_data, set_color_choice);
|
update_cursor_preview_common(responses, None, input, global_tool_data, set_color_choice);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = (&input, &global_tool_data, &render_mode);
|
||||||
|
|
||||||
|
// For Vello-rendered modes (Normal, Outline, and Pixel Preview), submit a backend render request
|
||||||
|
// which will return a zoomed-in pixel preview image via the EyedropperToolMessage::PreviewImage path
|
||||||
|
responses.add(PortfolioMessage::SubmitEyedropperPreviewRender);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_cursor_preview_common(
|
fn update_cursor_preview_common(
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,6 @@ impl NodeGraphExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[cfg(not(target_family = "wasm"))]
|
|
||||||
pub(crate) fn submit_eyedropper_preview(
|
pub(crate) fn submit_eyedropper_preview(
|
||||||
&mut self,
|
&mut self,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
|
|
@ -201,13 +200,25 @@ impl NodeGraphExecutor {
|
||||||
resolution: viewport_resolution,
|
resolution: viewport_resolution,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: On desktop, SVG Preview mode cannot work with the Eyedropper tool until <https://github.com/GraphiteEditor/Graphite/issues/3796> is implemented.
|
||||||
|
// TODO: So for now, we fall back to the Eyedropper using Normal mode (Vello) rendering, which looks similar enough to SVG Preview.
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
let render_mode = match document.render_mode {
|
||||||
|
graphene_std::vector::style::RenderMode::SvgPreview => graphene_std::vector::style::RenderMode::Normal,
|
||||||
|
other => other,
|
||||||
|
};
|
||||||
|
// On web, SVG Preview is handled by the frontend's SVG rasterization path instead, producing the correct result, so we keep it enabled.
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
let render_mode = document.render_mode;
|
||||||
|
|
||||||
let render_config = RenderConfig {
|
let render_config = RenderConfig {
|
||||||
viewport,
|
viewport,
|
||||||
scale: viewport_scale,
|
scale: viewport_scale,
|
||||||
time,
|
time,
|
||||||
pointer,
|
pointer,
|
||||||
export_format: graphene_std::application_io::ExportFormat::Raster,
|
export_format: graphene_std::application_io::ExportFormat::Raster,
|
||||||
render_mode: document.render_mode,
|
render_mode,
|
||||||
hide_artboards: false,
|
hide_artboards: false,
|
||||||
for_export: false,
|
for_export: false,
|
||||||
for_eyedropper: true,
|
for_eyedropper: true,
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,10 @@ impl NodeRuntime {
|
||||||
self.sender.send_eyedropper_preview(raster_cpu);
|
self.sender.send_eyedropper_preview(raster_cpu);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Eyedropper render that didn't produce a texture (e.g., SVG fallback when GPU is unavailable); discard it
|
||||||
|
_ if render_config.for_eyedropper => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
#[cfg(all(target_family = "wasm", feature = "gpu"))]
|
#[cfg(all(target_family = "wasm", feature = "gpu"))]
|
||||||
Ok(TaggedValue::RenderOutput(RenderOutput {
|
Ok(TaggedValue::RenderOutput(RenderOutput {
|
||||||
data: RenderOutputType::Texture(image_texture),
|
data: RenderOutputType::Texture(image_texture),
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@
|
||||||
const outsideArtboards = `<rect x="0" y="0" width="100%" height="100%" fill="${outsideArtboardsColor}" />`;
|
const outsideArtboards = `<rect x="0" y="0" width="100%" height="100%" fill="${outsideArtboardsColor}" />`;
|
||||||
|
|
||||||
const svg = `
|
const svg = `
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">${outsideArtboards}${artworkSvg}</svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:graphite="https://graphite.art" width="${width}" height="${height}">${outsideArtboards}${artworkSvg}</svg>
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
if (!rasterizedCanvas) {
|
if (!rasterizedCanvas) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue