1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
import { ClientUtils } from '../../../ClientUtils';
import { Doc } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { Upload } from '../../../server/SharedMediaTypes';
import { Docs, DocumentOptions } from '../../documents/Documents';
import { Networking } from '../../Network';
export namespace ImageUtils {
export const ExtractImgInfo = async (document: Doc) => {
const field = Cast(document.data, ImageField);
return field ? (Networking.PostToServer('/inspectImage', { source: field.url.href }) as Promise<Upload.InspectionResults>) : undefined;
};
export const AssignImgInfo = (document: Doc, data?: Upload.InspectionResults) => {
if (data) {
data.nativeWidth && (document._height = (NumCast(document._width) * data.nativeHeight) / data.nativeWidth);
const field = '$' + Doc.LayoutDataKey(document);
document[`${field}_nativeWidth`] = data.nativeWidth;
document[`${field}_nativeHeight`] = data.nativeHeight;
document[`${field}_path`] = data.source;
document[`${field}_exif`] = JSON.stringify(data.exifData.data);
document[`${field}_contentSize`] = data.contentSize ? data.contentSize : undefined;
}
return document;
};
export const ExportHierarchyToFileSystem = async (collection: Doc): Promise<void> => {
const a = document.createElement('a');
a.href = ClientUtils.prepend(`/imageHierarchyExport/${collection[Id]}`);
a.download = `Dash Export [${StrCast(collection.title)}].zip`;
a.click();
};
export function convertImgBlobToDataURL(blob: unknown) {
return new Promise<string | ArrayBuffer | null>((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = e => reject(e);
reader.readAsDataURL(blob as Blob);
});
}
export function createImageDocFromBlob(blob: Blob | undefined, options: DocumentOptions & { _nativeWidth: number; _nativeHeight: number }, filename: string, overwriteDoc?: Doc): Promise<Doc> {
return new Promise((resolve, reject) => {
if (!blob) return reject('No image blob provided');
convertImgBlobToDataURL(blob)
.then(durl => {
ClientUtils.convertDataUri(durl as string, filename)
.then(url => {
const imageSnapshot = Docs.Create.ImageDocument(url, options, overwriteDoc);
Doc.SetNativeWidth(imageSnapshot[DocData], options._nativeWidth);
Doc.SetNativeHeight(imageSnapshot[DocData], options._nativeHeight);
resolve(imageSnapshot);
})
.catch(reject);
})
.catch(reject);
});
}
let backgroundRemovalWorker: Worker | undefined;
const workerCallbackMap = new Map<string, { res: (imgBlob: Blob) => unknown; rej: () => unknown }>();
function getBackgroundRemovalWorker() {
if (backgroundRemovalWorker) return backgroundRemovalWorker;
backgroundRemovalWorker = new Worker('/image.worker.js', { type: 'module' });
backgroundRemovalWorker.onerror = (e: ErrorEvent) => {
const docId = (e as ErrorEvent & { docId: string }).docId;
const map = workerCallbackMap.get(docId);
map?.rej();
workerCallbackMap.delete(docId); // worker.terminate();
};
backgroundRemovalWorker.onmessage = async (event: MessageEvent) => {
if (event.data.type === 'progress') {
// Handle progress updates if needed
console.log(`Progress for docId ${event.data.docId}: ${event.data.progress}`);
} else {
const map = workerCallbackMap.get(event.data.docId);
event.data.success ? map?.res(event.data.result) : map?.rej();
workerCallbackMap.delete(event.data.docId); // worker.terminate();
}
};
return backgroundRemovalWorker;
}
export function removeImgBackground(docId: string, imagePath: string) {
return new Promise<Blob | undefined>((res, rej) => {
if (!imagePath) return rej('No image path provided');
workerCallbackMap.set(docId, { res, rej }); // Store the callback by docId (or use a unique requestId)
getBackgroundRemovalWorker().postMessage({
imagePath,
docId,
config: {
output: {
quality: 0.8, // The quality. (Default: 0.8)
},
},
});
});
}
export function maskForeground(docId: string, imagePath: string) {
return new Promise<Blob | undefined>((res, rej) => {
if (!imagePath) return rej('No image path provided');
workerCallbackMap.set(docId, { res, rej }); // Store the callback by docId (or use a unique requestId)
getBackgroundRemovalWorker().postMessage({
imagePath,
docId,
config: {
//publicPath: string; // The public path used for model and wasm files. Default: 'https://staticimgly.com/${PACKAGE_NAME}-data/${PACKAGE_VERSION}/dist/'
//debug: bool; // enable or disable useful console.log outputs
//device: 'gpu', // 'cpu' | 'gpu'; // choose the execution device. gpu will use webgpu if available
//model: 'isnet' | 'isnet_fp16' | 'isnet_quint8'; // The model to use. (Default "isnet_fp16")
output: {
//format: 'image/png' | 'image/jpeg' | 'image/webp'; // The output format. (Default "image/png")
quality: 0.8, // The quality. (Default: 0.8)
type: 'mask', // 'foreground' | 'background' | 'mask'; // The output type. (Default "foreground")
},
},
});
});
}
}
|