Brush tool live preview (#1116)

* Disable vector preview for brush tool

* Fix brush preview

* Fix warping

* Left and right square brackets to change size

* Add linear interpolation

* Modfiy existing selected brush layer

* Resolve warnings

---------

Co-authored-by: Dennis Kobert <dennis@kobert.dev>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2023-04-12 18:50:42 +01:00
committed by Keavon Chambers
parent 69f6ed3a11
commit f271964bf8
20 changed files with 130 additions and 58 deletions

View File

@@ -98,11 +98,13 @@ fn handle_message(message: String) -> String {
for image in image_data {
let path = image.path.clone();
let mime = image.mime.clone();
let transform = image.transform.clone();
images.insert(format!("{:?}_{}", &image.path, document_id), image);
stub_data.push(FrontendImageData {
path,
mime,
image_data: Arc::new(Vec::new()),
transform,
});
}
FrontendMessage::UpdateImageData { document_id, image_data: stub_data }

View File

@@ -529,6 +529,7 @@
}
&::placeholder {
opacity: 1;
color: inherit;
font-style: italic;
}

View File

@@ -113,7 +113,7 @@ export function createPortfolioState(editor: Editor) {
image.src = blobURL;
await image.decode();
editor.instance.setImageBlobURL(updateImageData.documentId, element.path, blobURL, image.naturalWidth, image.naturalHeight);
editor.instance.setImageBlobURL(updateImageData.documentId, element.path, blobURL, image.naturalWidth, image.naturalHeight, element.transform);
});
});
editor.subscriptions.subscribeJsMessage(TriggerNodeGraphFrameGenerate, async (triggerNodeGraphFrameGenerate) => {

View File

@@ -11,7 +11,7 @@ export type Editor = Readonly<ReturnType<typeof createEditor>>;
let wasmImport: WasmRawInstance | undefined;
let editorInstance: WasmEditorInstance | undefined;
export async function updateImage(path: BigUint64Array, mime: string, imageData: Uint8Array, documentId: bigint): Promise<void> {
export async function updateImage(path: BigUint64Array, mime: string, imageData: Uint8Array, transform: Float64Array, documentId: bigint): Promise<void> {
const blob = new Blob([imageData], { type: mime });
const blobURL = URL.createObjectURL(blob);
@@ -21,7 +21,7 @@ export async function updateImage(path: BigUint64Array, mime: string, imageData:
image.src = blobURL;
await image.decode();
editorInstance?.setImageBlobURL(documentId, path, blobURL, image.naturalWidth, image.naturalHeight);
editorInstance?.setImageBlobURL(documentId, path, blobURL, image.naturalWidth, image.naturalHeight,transform);
}
export async function fetchImage(path: BigUint64Array, mime: string, documentId: bigint, url: string): Promise<void> {
@@ -35,7 +35,7 @@ export async function fetchImage(path: BigUint64Array, mime: string, documentId:
image.src = blobURL;
await image.decode();
editorInstance?.setImageBlobURL(documentId, path, blobURL, image.naturalWidth, image.naturalHeight);
editorInstance?.setImageBlobURL(documentId, path, blobURL, image.naturalWidth, image.naturalHeight, undefined);
}
const tauri = "__TAURI_METADATA__" in window && import("@tauri-apps/api");

View File

@@ -768,6 +768,8 @@ export class ImaginateImageData {
readonly mime!: string;
readonly imageData!: Uint8Array;
readonly transform!: Float64Array ;
}
export class DisplayDialogDismiss extends JsMessage {}

View File

@@ -32,7 +32,7 @@ pub fn set_random_seed(seed: u64) {
/// This avoids creating a json with a list millions of numbers long.
#[wasm_bindgen(module = "@graphite/wasm-communication/editor")]
extern "C" {
fn updateImage(path: Vec<u64>, mime: String, imageData: &[u8], document_id: u64);
fn updateImage(path: Vec<u64>, mime: String, imageData: &[u8], transform: js_sys::Float64Array, document_id: u64);
fn fetchImage(path: Vec<u64>, mime: String, document_id: u64, identifier: String);
//fn dispatchTauri(message: String) -> String;
fn dispatchTauri(message: String);
@@ -114,7 +114,16 @@ impl JsEditorHandle {
if let FrontendMessage::UpdateImageData { document_id, image_data } = message {
for image in image_data {
#[cfg(not(feature = "tauri"))]
updateImage(image.path, image.mime, &image.image_data, document_id);
{
let transform = if let Some(transform_val) = image.transform {
let transform = js_sys::Float64Array::new_with_length(6);
transform.copy_from(&transform_val);
transform
} else {
js_sys::Float64Array::default()
};
updateImage(image.path, image.mime, &image.image_data, transform, document_id);
}
#[cfg(feature = "tauri")]
fetchImage(image.path.clone(), image.mime, document_id, format!("http://localhost:3001/image/{:?}_{}", &image.path, document_id));
}
@@ -495,15 +504,22 @@ impl JsEditorHandle {
/// Sends the blob URL generated by JS to the Image layer
#[wasm_bindgen(js_name = setImageBlobURL)]
pub fn set_image_blob_url(&self, document_id: u64, layer_path: Vec<LayerId>, blob_url: String, width: f64, height: f64) {
pub fn set_image_blob_url(&self, document_id: u64, layer_path: Vec<LayerId>, blob_url: String, width: f64, height: f64, transform: Option<js_sys::Float64Array>) {
let resolution = (width, height);
let message = PortfolioMessage::SetImageBlobUrl {
document_id,
layer_path,
layer_path: layer_path.clone(),
blob_url,
resolution,
};
self.dispatch(message);
if let Some(array) = transform.filter(|array| array.length() == 6) {
let mut transform: [f64; 6] = [0.; 6];
array.copy_to(&mut transform);
let message = document_legacy::Operation::SetLayerTransform { path: layer_path, transform };
self.dispatch(message);
}
}
/// Sends the blob URL generated by JS to the Imaginate layer in the respective document