diff options
Diffstat (limited to 'src/client/views/nodes/ImageBox.tsx')
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 71 |
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 = |