aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ImageBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/ImageBox.tsx')
-rw-r--r--src/client/views/nodes/ImageBox.tsx71
1 files changed, 62 insertions, 9 deletions
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 617a09ed5..cc747eb32 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -8,7 +8,7 @@ import { extname } from 'path';
import * as React from 'react';
import { AiOutlineSend } from 'react-icons/ai';
import ReactLoading from 'react-loading';
-import { ClientUtils, imageUrlToBase64, DashColor, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon, returnTrue } from '../../../ClientUtils';
+import { ClientUtils, DashColor, imageUrlToBase64, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
@@ -16,10 +16,11 @@ import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, DocCast, ImageCast, NumCast, RTFCast, StrCast, ImageCastWithSuffix } from '../../../fields/Types';
+import { Cast, DocCast, ImageCast, ImageCastWithSuffix, NumCast, RTFCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction } from '../../../Utils';
+import { gptImageLabel } from '../../apis/gpt/GPT';
import { Docs } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { DocUtils, FollowLinkScript } from '../../documents/DocUtils';
@@ -45,7 +46,6 @@ import { FieldView, FieldViewProps } from './FieldView';
import { FocusViewOptions } from './FocusViewOptions';
import './ImageBox.scss';
import { OpenWhere } from './OpenWhere';
-import { gptImageLabel } from '../../apis/gpt/GPT';
const DefaultPath = '/assets/unknown-file-icon-hi.png';
export class ImageEditorData {
@@ -389,7 +389,59 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return cropping;
};
- docEditorView = action(() => {
+ static _worker?: Worker;
+ static removeImgBackground = (doc: Doc, addDoc: (doc: Doc | Doc[], annotationKey?: string) => boolean, docImgPath: string) => {
+ ImageEditorData.AddDoc = addDoc;
+ ImageEditorData.RootDoc = doc;
+ if (ImageBox._worker) return ImageBox._worker;
+ const worker = new Worker('/image.worker.js', { type: 'module' });
+ worker.onmessage = async (event: MessageEvent) => {
+ const { success, result, error } = event.data;
+ if (success) {
+ const blobToDataURL = (blob: any) => {
+ return new Promise<string | ArrayBuffer | null>((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = () => resolve(reader.result);
+ reader.onerror = error => reject(error);
+ reader.readAsDataURL(blob);
+ });
+ };
+ blobToDataURL(result).then(durl => {
+ ClientUtils.convertDataUri(durl as string, doc[Id] + '_noBgd').then(url => {
+ const width = NumCast(doc._width) || 1;
+ const height = NumCast(doc._height);
+ const imageSnapshot = Docs.Create.ImageDocument(url, {
+ _nativeWidth: Doc.NativeWidth(doc),
+ _nativeHeight: Doc.NativeHeight(doc),
+ x: NumCast(doc.x) + width,
+ y: NumCast(doc.y),
+ _width: 150,
+ _height: (height / width) * 150,
+ title: 'bgremoved:' + doc.title,
+ });
+ Doc.SetNativeWidth(imageSnapshot[DocData], Doc.NativeWidth(doc));
+ Doc.SetNativeHeight(imageSnapshot[DocData], Doc.NativeHeight(doc));
+ addDoc?.(imageSnapshot);
+ });
+ });
+ } else {
+ console.error('Error in background removal:', error);
+ }
+ // worker.terminate();
+ };
+ worker.onerror = (e: ErrorEvent) => console.error('Worker failed:', e); // worker.terminate();
+
+ worker.postMessage({ imagePath: docImgPath });
+ return worker;
+ };
+ removeBackground = () => {
+ const field = ImageCast(this.dataDoc[this.fieldKey]);
+ if (field && this._props.addDocument) {
+ ImageBox.removeImgBackground(this.rootDoc, this._props.addDocument, this.choosePath(field.url));
+ }
+ };
+
+ docEditorView = () => {
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (field) {
ImageEditorData.Open = true;
@@ -397,7 +449,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ImageEditorData.AddDoc = this._props.addDocument;
ImageEditorData.RootDoc = this.Document;
}
- });
+ };
@observable _showOutpaintPrompt: boolean = false;
@observable _outpaintPromptInput: string = 'Extend this image naturally with matching content';
@@ -433,7 +485,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
processOutpaintingWithPrompt = async (customPrompt: string) => {
- const field = Cast(this.dataDoc[this.fieldKey], ImageField);
+ const field = ImageCast(this.dataDoc[this.fieldKey]);
if (!field) return;
// Set flag that outpainting is in progress
@@ -618,7 +670,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
);
specificContextMenu = (): void => {
- const field = Cast(this.dataDoc[this.fieldKey], ImageField);
+ const field = ImageCast(this.dataDoc[this.fieldKey]);
if (field) {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'redo-alt' });
@@ -629,13 +681,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
file: (file => {
const ext = file ? extname(file) : '';
return file?.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
- })(ImageCast(this.Document[Doc.LayoutDataKey(this.Document)])?.url.href),
+ })(ImageCast(this.Document[this.fieldKey])?.url.href),
}).then(text => alert(text));
},
icon: 'expand-arrows-alt',
}); // prettier-ignore
funcs.push({ description: 'Copy path', event: () => ClientUtils.CopyText(this.choosePath(field.url)), icon: 'copy' });
funcs.push({ description: 'Open Image Editor', event: this.docEditorView, icon: 'pencil-alt' });
+ funcs.push({ description: 'Remove Background', event: this.removeBackground, icon: 'pencil-alt' });
this.layoutDoc.ai &&
funcs.push({
description: 'Regenerate AI Image',
@@ -800,7 +853,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
@computed get paths() {
- const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey])); // retrieve the primary image URL that is being rendered from the data doc
+ const field = ImageCast(this.dataDoc[this.fieldKey], new ImageField(StrCast(this.dataDoc[this.fieldKey]))); // retrieve the primary image URL that is being rendered from the data doc
const alts = DocListCast(this.dataDoc[this.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
const defaultUrl = new URL(ClientUtils.prepend(DefaultPath));
const altpaths =