Skip to content

Commit

Permalink
feat(vectorizer): Simplify and move into worker
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielHauschildt committed Feb 7, 2024
1 parent 5beb7c3 commit 1f17f48
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 274 deletions.
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}"
}
]
}
9 changes: 5 additions & 4 deletions packages/vectorizer/esbuild/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@ console.log(

const configs = [
{
entryPoints: ['src/index.ts'],
entryPoints: ['src/index.ts', "src/worker.ts"],
define: {
PLUGIN_VERSION: `"${packageJson.version}"`
},
minify: true,
bundle: true,
sourcemap: true,
external: ['@cesdk/cesdk-js', 'lodash', "node:path", "fs", "url"],
platform: 'browser',
external: ['@cesdk/cesdk-js'],
platform: 'node',
format: 'esm',
outfile: 'dist/index.mjs',
outdir: 'dist',
outExtension: { '.js': '.mjs' },
plugins: [
{
name: 'reporter',
Expand Down
2 changes: 1 addition & 1 deletion packages/vectorizer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@cesdk/cesdk-js": "~1.20.0"
},
"dependencies": {
"@imgly/vectorizer": "^0.1.0-rc4",
"@imgly/vectorizer": "../vectorizer/packages/js",
"lodash": "^4.17.21"
}
}
155 changes: 155 additions & 0 deletions packages/vectorizer/src/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import type CreativeEditorSDK from '@cesdk/cesdk-js';

import {
getPluginMetadata,
isMetadataConsistent,
recoverInitialImageData,
setPluginMetadata
} from './utils';


const runInWorker = (uri: string) => new Promise<Blob>((resolve, reject) => {
const worker = new Worker(new URL('./worker', import.meta.url), { type: 'module' });
worker.postMessage({data: uri})
worker.onmessage = (e: MessageEvent) => {
const msg = e.data
if (msg.error) {
reject (msg.error)
return;
}
resolve(new Blob([msg.data]))
// when done terminate
worker.terminate()
}

})


/**
* Apply the vectorization process to the image.
*/

/**
* Triggers the vectiorize process.
*/
export async function command(
cesdk: CreativeEditorSDK,
blockId: number
) {
const engine = cesdk.engine; // the only function that needs the ui is the upload function
const blockApi = cesdk.engine.block;
if (!blockApi.hasFill(blockId))
throw new Error('Block has no fill to vectorize');

const fillId = blockApi.getFill(blockId);

// Get the current image URI and source set as initial values.
const initialSourceSet = blockApi.getSourceSet(
fillId,
'fill/image/sourceSet'
);
const initialImageFileURI = blockApi.getString(
fillId,
'fill/image/imageFileURI'
);
const initialPreviewFileURI = blockApi.getString(
fillId,
'fill/image/previewFileURI'
);


const uriToProcess =
// Source sets have priority in the engine
initialSourceSet.length > 0
? // Choose the highest resolution image in the source set
initialSourceSet.sort(
(a, b) => b.width * b.height - a.height * a.width
)[0].uri
: initialImageFileURI;

if (uriToProcess === undefined || uriToProcess === '')
return; // We shall return early if the uri is not defined or invalid



try {
// Clear values in the engine to trigger the loading spinner
+9
blockApi.setString(fillId, 'fill/image/imageFileURI', '');
blockApi.setSourceSet(fillId, 'fill/image/sourceSet', []);
// ensure we show the last image while processsing. Some images don't have the preview set
if (initialPreviewFileURI === undefined || initialPreviewFileURI === '') {
blockApi.setString(fillId, 'fill/image/previewFileURI', uriToProcess);
}
const metadata = getPluginMetadata(engine, blockId);
setPluginMetadata(engine, blockId, {
...metadata,
version: PLUGIN_VERSION,
initialSourceSet,
initialImageFileURI,
blockId,
fillId,
status: 'PROCESSING'
});

const vectorized: Blob = await runInWorker(uriToProcess)

if (
getPluginMetadata(cesdk.engine, blockId).status !== 'PROCESSING' ||
!isMetadataConsistent(cesdk, blockId)
)
return;

const pathname = new URL(uriToProcess).pathname;
const parts = pathname.split('/');
const filename = parts[parts.length - 1];

const uploadedAssets = await cesdk.unstable_upload(
new File([vectorized], filename, { type: vectorized.type }),
() => {
// TODO Delegate process to UI component
}
);

// Check for externally changed state while we were uploading and
// do not proceed if the state was reset.
if (
getPluginMetadata(engine, blockId).status !== 'PROCESSING' ||
!isMetadataConsistent(cesdk, blockId)
)
return;

const url = uploadedAssets.meta?.uri;
if (url == null) {
throw new Error('Could not upload vectorized image');
}

setPluginMetadata(engine, blockId, {
version: PLUGIN_VERSION,
initialSourceSet,
initialImageFileURI,
blockId,
fillId,
status: 'PROCESSED_TOGGLE_ON',
processedAsset: url
});
blockApi.setString(fillId, 'fill/image/imageFileURI', url);
// Finally, create an undo step
cesdk.engine.editor.addUndoStep();
} catch (error) {
if (cesdk.engine.block.isValid(blockId)) {
setPluginMetadata(engine, blockId, {
version: PLUGIN_VERSION,
initialSourceSet,
initialImageFileURI,
blockId,
fillId,
status: 'ERROR'
});

recoverInitialImageData(cesdk, blockId);
}
// eslint-disable-next-line no-console
console.error(error);
}
}
16 changes: 8 additions & 8 deletions packages/vectorizer/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export const PLUGIN_ID = '@imgly/plugin-vectorizer-web';
export const CANVAS_MENU_COMPONENT_ID = `${PLUGIN_ID}.canvasMenu`;
export const CANVAS_MENU_COMPONENT_BUTTON_ID = `${CANVAS_MENU_COMPONENT_ID}.button`;
export const FEATURE_ID = `${PLUGIN_ID}.feature`;
export const I18N_ID = "plugin.vectorizer.vectorize"
export const I18N_TRANSLATIONS = {
en: { [I18N_ID]: 'Vectorize' },
de: { [I18N_ID]: 'Vektorisieren' }
export const PLUGIN_CANVAS_MENU_COMPONENT_ID = `${PLUGIN_ID}.canvasMenu`;
export const PLUGIN_CANVAS_MENU_COMPONENT_BUTTON_ID = `${PLUGIN_CANVAS_MENU_COMPONENT_ID}.button`;
export const PLUGIN_FEATURE_ID = `${PLUGIN_ID}`;
export const PLUGIN_I18N_ID = `plugin.${PLUGIN_ID}.vectorize`
export const PLUGIN_I18N_TRANSLATIONS = {
en: { [PLUGIN_I18N_ID]: 'Vectorize' },
de: { [PLUGIN_I18N_ID]: 'Vektorisieren' }
}
export const ICON = '@imgly/icons/Vectorize'
export const PLUGIN_ICON = '@imgly/icons/Vectorize'
28 changes: 0 additions & 28 deletions packages/vectorizer/src/enableFeatures.ts

This file was deleted.

87 changes: 57 additions & 30 deletions packages/vectorizer/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type CreativeEditorSDK from '@cesdk/cesdk-js';
import CreativeEditorSDK, { CreativeEngine } from '@cesdk/cesdk-js';

import { PLUGIN_FEATURE_ID, PLUGIN_ID } from './constants';

import { command } from './commands';
import { registerComponents } from './ui';

import { FEATURE_ID, PLUGIN_ID } from './constants';
import { enableFeatures } from './enableFeatures';
import { processVectorization } from './processVectorization';
import { registerComponents } from './registerComponents';
import {
clearPluginMetadata,
fixDuplicateMetadata,
Expand All @@ -12,34 +13,36 @@ import {
isMetadataConsistent
} from './utils';

export interface PluginConfiguration {}
export interface PluginConfiguration { }

export default (pluginConfiguration: PluginConfiguration = {}) => {
return {
initialize() {},
initialize(engine: CreativeEngine) {
engine.event.subscribe([], async (events) => {
events
.filter((e) => engine.block.isValid(e.block) && engine.block.hasMetadata(e.block, PLUGIN_ID))
.forEach((e) => {
const id = e.block;
if (e.type === 'Created') {
const metadata = getPluginMetadata(engine, id);
if (isDuplicate(engine, id, metadata)) {
fixDuplicateMetadata(engine, id);
}
}
});
});
},

update() {},
update() {

},

initializeUserInterface({ cesdk }: { cesdk: CreativeEditorSDK }) {
cesdk.engine.event.subscribe([], async (events) => {
events.forEach((e) => {
const id = e.block;
if (
!cesdk.engine.block.isValid(id) ||
!cesdk.engine.block.hasMetadata(id, PLUGIN_ID)
) {
return;
}

if (e.type === 'Created') {
const metadata = getPluginMetadata(cesdk, id);
if (isDuplicate(cesdk, id, metadata)) {
fixDuplicateMetadata(cesdk, id);
}
} else if (e.type === 'Updated') {
handleUpdateEvent(cesdk, id);
}
});
events
.filter((e) => cesdk.engine.block.isValid(e.block) && cesdk.engine.block.hasMetadata(e.block, PLUGIN_ID))
.filter((e) => e.type === 'Updated')
.forEach((e) => { handleUpdateEvent(cesdk, e.block); });
});

registerComponents(cesdk);
Expand All @@ -53,16 +56,16 @@ export default (pluginConfiguration: PluginConfiguration = {}) => {
* updated.
*/
async function handleUpdateEvent(cesdk: CreativeEditorSDK, blockId: number) {
const metadata = getPluginMetadata(cesdk, blockId);
const metadata = getPluginMetadata(cesdk.engine, blockId);

switch (metadata.status) {
case 'PENDING': {
if (
cesdk.feature.unstable_isEnabled(FEATURE_ID, {
cesdk.feature.unstable_isEnabled(PLUGIN_FEATURE_ID, {
engine: cesdk.engine
})
) {
processVectorization(cesdk, blockId);
command(cesdk, blockId);
}
break;
}
Expand All @@ -71,7 +74,7 @@ async function handleUpdateEvent(cesdk: CreativeEditorSDK, blockId: number) {
case 'PROCESSED_TOGGLE_OFF':
case 'PROCESSED_TOGGLE_ON': {
if (!isMetadataConsistent(cesdk, blockId)) {
clearPluginMetadata(cesdk, blockId);
clearPluginMetadata(cesdk.engine, blockId);
}
break;
}
Expand All @@ -81,3 +84,27 @@ async function handleUpdateEvent(cesdk: CreativeEditorSDK, blockId: number) {
}
}
}


export function enableFeatures(cesdk: CreativeEditorSDK) {
cesdk.feature.unstable_enable(PLUGIN_FEATURE_ID, ({ engine }) => {
const selectedIds = engine.block.findAllSelected();
if (selectedIds.length !== 1) {
return false;
}
const [selectedId] = selectedIds;

if (cesdk.engine.block.hasFill(selectedId)) {
const fillId = cesdk.engine.block.getFill(selectedId);
const fillType = cesdk.engine.block.getType(fillId);

if (fillType !== '//ly.img.ubq/fill/image') {
return false;
}
return true

}

return false;
});
}
Loading

0 comments on commit 1f17f48

Please sign in to comment.