mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 05:58:11 +08:00
Add inpainting and outpainting to Imaginate (#864)
* Do not select layer immediatly on drag * Add LayerReferenceInput MVP widget * Properties Panel * Fix dragging marker flicker * Change mask shape to outline * Add mask rendering * Simplify select code * Remove colours * Fix inpaint/outpaint and rearrage widget UX * Add mask blur and mask starting fill parameters * Guard for the case when the layer is missing * Add icon to LayerReferenceInput to finalize its UI Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
5bf7b9fdf8
commit
9d80defa14
@@ -963,23 +963,55 @@ impl DocumentMessageHandler {
|
||||
restore_faces: imaginate_layer.restore_faces,
|
||||
tiling: imaginate_layer.tiling,
|
||||
};
|
||||
let base_image = if imaginate_layer.use_img2img {
|
||||
let mask_paint_mode = imaginate_layer.mask_paint_mode;
|
||||
let mask_blur_px = imaginate_layer.mask_blur_px;
|
||||
let mask_fill_content = imaginate_layer.mask_fill_content;
|
||||
let (base_image, mask_image) = if imaginate_layer.use_img2img {
|
||||
let mask = imaginate_layer.mask_layer_ref.clone();
|
||||
|
||||
// Calculate the size of the region to be exported
|
||||
let size = DVec2::new(transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length());
|
||||
|
||||
let old_transforms = self.remove_document_transform();
|
||||
let svg = self.render_document(size, transform.inverse(), persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path));
|
||||
self.restore_document_transform(old_transforms);
|
||||
|
||||
Some(ImaginateBaseImage { svg, size })
|
||||
let mask_image = mask.and_then(|mask_layer_path| match self.graphene_document.layer(&mask_layer_path) {
|
||||
Ok(_) => {
|
||||
let svg = self.render_document(size, transform.inverse(), persistent_data, DocumentRenderMode::LayerCutout(&mask_layer_path, Color::WHITE));
|
||||
|
||||
Some(ImaginateBaseImage { svg, size })
|
||||
}
|
||||
Err(_) => None,
|
||||
});
|
||||
|
||||
if mask_image.is_none() {
|
||||
return Some(
|
||||
DialogMessage::DisplayDialogError {
|
||||
title: "Masking layer is missing".into(),
|
||||
description: "
|
||||
It may have been deleted or moved. Please drag a new layer reference\n\
|
||||
into the 'Masking Layer' parameter input, then generate again."
|
||||
.trim()
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
self.restore_document_transform(old_transforms);
|
||||
(Some(ImaginateBaseImage { svg, size }), mask_image)
|
||||
} else {
|
||||
None
|
||||
(None, None)
|
||||
};
|
||||
|
||||
Some(
|
||||
FrontendMessage::TriggerImaginateGenerate {
|
||||
parameters,
|
||||
base_image,
|
||||
mask_image,
|
||||
mask_paint_mode,
|
||||
mask_blur_px,
|
||||
mask_fill_content,
|
||||
hostname: preferences.imaginate_server_hostname.clone(),
|
||||
refresh_frequency: preferences.imaginate_refresh_frequency,
|
||||
document_id,
|
||||
@@ -1042,13 +1074,17 @@ impl DocumentMessageHandler {
|
||||
|
||||
let render_data = RenderData::new(ViewMode::Normal, &persistent_data.font_cache, None);
|
||||
|
||||
let artwork = match render_mode {
|
||||
DocumentRenderMode::Root => self.graphene_document.render_root(render_data),
|
||||
DocumentRenderMode::OnlyBelowLayerInFolder(below_layer_path) => self.graphene_document.render_layers_below(below_layer_path, render_data).unwrap(),
|
||||
let (artwork, outside) = match render_mode {
|
||||
DocumentRenderMode::Root => (self.graphene_document.render_root(render_data), None),
|
||||
DocumentRenderMode::OnlyBelowLayerInFolder(below_layer_path) => (self.graphene_document.render_layers_below(below_layer_path, render_data).unwrap(), None),
|
||||
DocumentRenderMode::LayerCutout(layer_path, background) => (self.graphene_document.render_layer(layer_path, render_data).unwrap(), Some(background)),
|
||||
};
|
||||
let artboards = self.artboard_message_handler.artboards_graphene_document.render_root(render_data);
|
||||
let outside_artboards_color = if self.artboard_message_handler.artboard_ids.is_empty() { "#ffffff" } else { "#222222" };
|
||||
let outside_artboards = format!(r#"<rect x="0" y="0" width="100%" height="100%" fill="{}" />"#, outside_artboards_color);
|
||||
let outside_artboards_color = outside.map_or_else(
|
||||
|| if self.artboard_message_handler.artboard_ids.is_empty() { "ffffff" } else { "222222" }.to_string(),
|
||||
|col| col.rgba_hex(),
|
||||
);
|
||||
let outside_artboards = format!(r##"<rect x="0" y="0" width="100%" height="100%" fill="#{}" />"##, outside_artboards_color);
|
||||
let matrix = transform
|
||||
.to_cols_array()
|
||||
.iter()
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::messages::layout::utility_types::widgets::assist_widgets::PivotPositi
|
||||
use crate::messages::portfolio::document::utility_types::misc::TargetDocument;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graphene::layers::imaginate_layer::ImaginateSamplingMethod;
|
||||
use graphene::layers::imaginate_layer::{ImaginateMaskFillContent, ImaginateMaskPaintMode, ImaginateSamplingMethod};
|
||||
use graphene::layers::style::{Fill, Stroke};
|
||||
use graphene::LayerId;
|
||||
|
||||
@@ -29,6 +29,10 @@ pub enum PropertiesPanelMessage {
|
||||
SetActiveLayers { paths: Vec<Vec<LayerId>>, document: TargetDocument },
|
||||
SetImaginateCfgScale { cfg_scale: f64 },
|
||||
SetImaginateDenoisingStrength { denoising_strength: f64 },
|
||||
SetImaginateLayerPath { layer_path: Option<Vec<LayerId>> },
|
||||
SetImaginateMaskBlurPx { mask_blur_px: u32 },
|
||||
SetImaginateMaskFillContent { mode: ImaginateMaskFillContent },
|
||||
SetImaginateMaskPaintMode { paint: ImaginateMaskPaintMode },
|
||||
SetImaginateNegativePrompt { negative_prompt: String },
|
||||
SetImaginatePrompt { prompt: String },
|
||||
SetImaginateRestoreFaces { restore_faces: bool },
|
||||
|
||||
+19
-2
@@ -140,10 +140,11 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||
}
|
||||
ResendActiveProperties => {
|
||||
if let Some((path, target_document)) = self.active_selection.clone() {
|
||||
let layer = get_document(target_document).layer(&path).unwrap();
|
||||
let document = get_document(target_document);
|
||||
let layer = document.layer(&path).unwrap();
|
||||
match target_document {
|
||||
TargetDocument::Artboard => register_artboard_layer_properties(layer, responses, persistent_data),
|
||||
TargetDocument::Artwork => register_artwork_layer_properties(path, layer, responses, persistent_data, node_graph_message_handler),
|
||||
TargetDocument::Artwork => register_artwork_layer_properties(document, path, layer, responses, persistent_data, node_graph_message_handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,6 +167,22 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||
responses.push_back(Operation::ImaginateSetDenoisingStrength { path, denoising_strength }.into());
|
||||
}
|
||||
SetImaginateLayerPath { layer_path } => {
|
||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||
responses.push_back(Operation::ImaginateSetLayerPath { path, layer_path }.into());
|
||||
}
|
||||
SetImaginateMaskBlurPx { mask_blur_px } => {
|
||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||
responses.push_back(Operation::ImaginateSetMaskBlurPx { path, mask_blur_px }.into());
|
||||
}
|
||||
SetImaginateMaskFillContent { mode } => {
|
||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||
responses.push_back(Operation::ImaginateSetMaskFillContent { path, mode }.into());
|
||||
}
|
||||
SetImaginateMaskPaintMode { paint } => {
|
||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||
responses.push_back(Operation::ImaginateSetMaskPaintMode { path, paint }.into());
|
||||
}
|
||||
SetImaginateSamples { samples } => {
|
||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||
responses.push_back(Operation::ImaginateSetSamples { path, samples }.into());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -61,12 +61,12 @@ impl LayerPanelEntry {
|
||||
pub fn new(layer_metadata: &LayerMetadata, transform: DAffine2, layer: &Layer, path: Vec<LayerId>, font_cache: &FontCache) -> Self {
|
||||
let name = layer.name.clone().unwrap_or_else(|| String::from(""));
|
||||
|
||||
let tooltip = if cfg!(debug_assertions) {
|
||||
let joined = &path.iter().map(|id| id.to_string()).collect::<Vec<_>>().join(" / ");
|
||||
name.clone() + "\nLayer Path: " + joined.as_str()
|
||||
} else {
|
||||
name.clone()
|
||||
};
|
||||
let mut tooltip = name.clone();
|
||||
if cfg!(debug_assertions) {
|
||||
tooltip += "\nLayer Path: ";
|
||||
tooltip += &path.iter().map(|id| id.to_string()).collect::<Vec<_>>().join(" / ");
|
||||
tooltip = tooltip.trim().to_string();
|
||||
}
|
||||
|
||||
let arr = layer.data.bounding_box(transform, font_cache).unwrap_or([DVec2::ZERO, DVec2::ZERO]);
|
||||
let arr = arr.iter().map(|x| (*x).into()).collect::<Vec<(f64, f64)>>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub use super::layer_panel::{LayerMetadata, LayerPanelEntry};
|
||||
|
||||
use graphene::color::Color;
|
||||
use graphene::document::Document as GrapheneDocument;
|
||||
use graphene::LayerId;
|
||||
|
||||
@@ -65,4 +66,5 @@ impl DocumentMode {
|
||||
pub enum DocumentRenderMode<'a> {
|
||||
Root,
|
||||
OnlyBelowLayerInFolder(&'a [LayerId]),
|
||||
LayerCutout(&'a [LayerId], Color),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user