mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 00:58:11 +08:00
Remake Eyedropper tool to sample pixel colors from viewport canvas (#801)
* Remake Eyedropper tool to sample pixel colors from viewport canvas * Bug fixes * Reorder export buttons * Remove the larger primary/secondary ring * Add aborting with Escape
This commit is contained in:
@@ -3,6 +3,7 @@ import { blobToBase64 } from "@/utility-functions/files";
|
||||
import { type RequestResult, requestWithUploadDownloadProgress } from "@/utility-functions/network";
|
||||
import { stripIndents } from "@/utility-functions/strip-indents";
|
||||
import { type Editor } from "@/wasm-communication/editor";
|
||||
import type { XY } from "@/wasm-communication/messages";
|
||||
import { type ImaginateGenerationParameters } from "@/wasm-communication/messages";
|
||||
|
||||
const MAX_POLLING_RETRIES = 4;
|
||||
@@ -73,7 +74,7 @@ export async function imaginateGenerate(
|
||||
|
||||
// Send the backend a blob URL for the final image
|
||||
const blobURL = URL.createObjectURL(blob);
|
||||
editor.instance.setImaginateBlobURL(documentId, layerPath, blobURL, parameters.resolution[0], parameters.resolution[1]);
|
||||
editor.instance.setImaginateBlobURL(documentId, layerPath, blobURL, parameters.resolution.x, parameters.resolution.y);
|
||||
|
||||
// Send the backend the blob data to be stored persistently in the layer
|
||||
const u8Array = new Uint8Array(await blob.arrayBuffer());
|
||||
@@ -134,7 +135,7 @@ function scheduleNextPollingUpdate(
|
||||
hostname: string,
|
||||
documentId: bigint,
|
||||
layerPath: BigUint64Array,
|
||||
resolution: [number, number]
|
||||
resolution: XY
|
||||
): void {
|
||||
// Pick a future time that keeps to the user-requested interval if possible, but on slower connections will go as fast as possible without overlapping itself
|
||||
const nextPollTimeGoal = timeoutBegan + interval;
|
||||
@@ -148,7 +149,7 @@ function scheduleNextPollingUpdate(
|
||||
if (terminated) return;
|
||||
|
||||
const blobURL = URL.createObjectURL(blob);
|
||||
editor.instance.setImaginateBlobURL(documentId, layerPath, blobURL, resolution[0], resolution[1]);
|
||||
editor.instance.setImaginateBlobURL(documentId, layerPath, blobURL, resolution.x, resolution.y);
|
||||
editor.instance.setImaginateGeneratingStatus(documentId, layerPath, percentComplete, "Generating");
|
||||
|
||||
scheduleNextPollingUpdate(interval, nextTimeoutBegan, 0, editor, hostname, documentId, layerPath, resolution);
|
||||
@@ -244,8 +245,8 @@ async function generate(
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
${parameters.resolution[1]},
|
||||
${parameters.resolution[0]},
|
||||
${parameters.resolution.y},
|
||||
${parameters.resolution.x},
|
||||
false,
|
||||
0.7,
|
||||
0,
|
||||
@@ -301,8 +302,8 @@ async function generate(
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
${parameters.resolution[1]},
|
||||
${parameters.resolution[0]},
|
||||
${parameters.resolution.y},
|
||||
${parameters.resolution.x},
|
||||
"Just resize",
|
||||
false,
|
||||
32,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { replaceBlobURLsWithBase64 } from "@/utility-functions/files";
|
||||
|
||||
// Rasterize the string of an SVG document at a given width and height and turn it into the blob data of an image file matching the given MIME type
|
||||
export async function rasterizeSVG(svg: string, width: number, height: number, mime: string, backgroundColor?: string): Promise<Blob> {
|
||||
let promiseResolve: (value: Blob | PromiseLike<Blob>) => void | undefined;
|
||||
let promiseReject: () => void | undefined;
|
||||
const promise = new Promise<Blob>((resolve, reject) => {
|
||||
export async function rasterizeSVGCanvas(svg: string, width: number, height: number, backgroundColor?: string): Promise<HTMLCanvasElement> {
|
||||
let promiseResolve: (value: HTMLCanvasElement | PromiseLike<HTMLCanvasElement>) => void | undefined;
|
||||
const promise = new Promise<HTMLCanvasElement>((resolve) => {
|
||||
promiseResolve = resolve;
|
||||
promiseReject = reject;
|
||||
});
|
||||
|
||||
// A canvas to render our svg to in order to get a raster image
|
||||
@@ -14,8 +12,8 @@ export async function rasterizeSVG(svg: string, width: number, height: number, m
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const context = canvas.getContext("2d");
|
||||
if (!context) return Promise.reject();
|
||||
const context = canvas.getContext("2d", { willReadFrequently: true });
|
||||
if (!context) throw new Error("Can't create 2D context from canvas during SVG rasterization");
|
||||
|
||||
// Apply a background fill color if one is given
|
||||
if (backgroundColor) {
|
||||
@@ -37,13 +35,28 @@ export async function rasterizeSVG(svg: string, width: number, height: number, m
|
||||
// Clean up the SVG blob URL (once the URL is revoked, the SVG blob data itself is garbage collected after `svgBlob` goes out of scope)
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
// Convert the canvas to an image of the correct MIME type
|
||||
canvas.toBlob((blob) => {
|
||||
if (blob !== null) promiseResolve(blob);
|
||||
else promiseReject();
|
||||
}, mime);
|
||||
promiseResolve(canvas);
|
||||
};
|
||||
image.src = url;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
export async function rasterizeSVG(svg: string, width: number, height: number, mime: string, backgroundColor?: string): Promise<Blob> {
|
||||
let promiseResolve: (value: Blob | PromiseLike<Blob>) => void | undefined;
|
||||
let promiseReject: () => void | undefined;
|
||||
const promise = new Promise<Blob>((resolve, reject) => {
|
||||
promiseResolve = resolve;
|
||||
promiseReject = reject;
|
||||
});
|
||||
|
||||
rasterizeSVGCanvas(svg, width, height, backgroundColor).then((canvas) => {
|
||||
// Convert the canvas to an image of the correct MIME type
|
||||
canvas.toBlob((blob) => {
|
||||
if (blob !== null) promiseResolve(blob);
|
||||
else promiseReject();
|
||||
}, mime);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user