Generating processor metadata

This commit is contained in:
Jake Archibald
2020-11-06 10:25:26 +00:00
parent 0d298e3e0a
commit 4b8c0178fe
17 changed files with 114 additions and 28 deletions

View File

@@ -1 +0,0 @@
The compress folder was moved from src/client/lazy-app.

View File

@@ -22,7 +22,7 @@ const autoGenComment =
export default function () {
let previousWorkerImports;
let previousEncoderMetas;
let previousJoinedMetas;
/**
* Generates the worker file & tsconfig for all features
@@ -134,7 +134,7 @@ export default function () {
const joinedWorkerImports = workerImports.join();
// Avoid regenerating if nothing's changed.
// This also prevents an infinite look in the watcher.
// This also prevents an infinite loop in the watcher.
if (joinedWorkerImports === previousWorkerImports) return;
previousWorkerImports = joinedWorkerImports;
@@ -153,6 +153,14 @@ export default function () {
.filter((tsFile) => !tsFile.endsWith('.d.ts'))
.map((tsFile) => tsFile.slice(0, -'.ts'.length));
const processorMetas = (
await globP('src/features/processors/*/shared/meta.ts', {
absolute: true,
})
)
.filter((tsFile) => !tsFile.endsWith('.d.ts'))
.map((tsFile) => tsFile.slice(0, -'.ts'.length));
const featureMetaBasePath = path.join(
process.cwd(),
'src',
@@ -161,20 +169,24 @@ export default function () {
'feature-meta',
);
const joinedEncoderMetas = encoderMetas.join();
const joinedMetas = [...encoderMetas, ...processorMetas].join();
// Avoid regenerating if nothing's changed.
// This also prevents an infinite look in the watcher.
if (joinedEncoderMetas === previousEncoderMetas) return;
previousEncoderMetas = joinedEncoderMetas;
// This also prevents an infinite loop in the watcher.
if (joinedMetas === previousJoinedMetas) return;
previousJoinedMetas = joinedMetas;
const encoderMetaTsNames = encoderMetas.map((tsImport) => [
const getTsName = (tsImport) => [
path.relative(featureMetaBasePath, tsImport),
path.basename(tsImport.slice(0, -'/shared/meta'.length)),
]);
];
const encoderMetaTsNames = encoderMetas.map(getTsName);
const processorMetaTsNames = processorMetas.map(getTsName);
const featureMeta = [
autoGenComment,
// Encoder stuff
encoderMetaTsNames.map(
([path, name]) => `import * as ${name}EncoderMeta from '${path}';`,
),
@@ -194,6 +206,22 @@ export default function () {
`};`,
`export type EncoderType = keyof typeof encoderMap`,
`export const encoders = [...Object.values(encoderMap)];`,
// Processor stuff
processorMetaTsNames.map(
([path, name]) => `import * as ${name}ProcessorMeta from '${path}';`,
),
`interface Enableable { enabled: boolean; }`,
`export interface ProcessorState {`,
processorMetaTsNames.map(
([_, name]) => ` ${name}: Enableable & ${name}ProcessorMeta.Options;`,
),
`}`,
`export const defaultProcessorState: ProcessorState = {`,
processorMetaTsNames.map(
([_, name]) =>
` ${name}: { enabled: false, ...${name}ProcessorMeta.defaultOptions },`,
),
`}`,
]
.flat(Infinity)
.join('\n');

View File

@@ -1,6 +1,5 @@
import { EncoderState } from '../feature-meta';
import { shallowEqual } from '../../lib/util';
import { PreprocessorState } from '../../codecs/preprocessors';
import { EncoderState, ProcessorState } from '../feature-meta';
import { shallowEqual } from '../../util';
import * as identity from '../../codecs/identity/encoder-meta';
@@ -11,7 +10,7 @@ interface CacheResult {
}
interface CacheEntry extends CacheResult {
preprocessorState: PreprocessorState;
processorState: ProcessorState;
encoderState: EncoderState;
sourceData: ImageData;
}
@@ -32,7 +31,7 @@ export default class ResultCache {
match(
sourceData: ImageData,
preprocessorState: PreprocessorState,
processorState: ProcessorState,
encoderState: EncoderState,
): CacheResult | undefined {
const matchingIndex = this._entries.findIndex((entry) => {
@@ -41,11 +40,11 @@ export default class ResultCache {
if (entry.encoderState.type !== encoderState.type) return false;
// Check that each set of options in the preprocessor are the same
for (const prop in preprocessorState) {
for (const prop in processorState) {
if (
!shallowEqual(
(preprocessorState as any)[prop],
(entry.preprocessorState as any)[prop],
(processorState as any)[prop],
(entry.processorState as any)[prop],
)
)
return false;

View File

@@ -59,3 +59,13 @@ export async function abortable<T>(
}),
]);
}
/**
* Compare two objects, returning a boolean indicating if they have the same properties and strictly
* equal values.
*/
export function shallowEqual(one: any, two: any) {
for (const i in one) if (one[i] !== two[i]) return false;
for (const i in two) if (!(i in one)) return false;
return true;
}

View File

@@ -0,0 +1,23 @@
/**
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface Options {
zx: number;
maxNumColors: number;
dither: number;
}
export const defaultOptions: Options = {
zx: 0,
maxNumColors: 256,
dither: 1.0,
};

View File

@@ -0,0 +1,13 @@
/**
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference path="../../../../../missing-types.d.ts" />

View File

@@ -0,0 +1,7 @@
{
"extends": "../../../../../generic-tsconfig.json",
"compilerOptions": {
"lib": ["webworker", "esnext"]
},
"references": [{ "path": "../../../" }]
}

View File

@@ -13,18 +13,13 @@
import imagequant, { QuantizerModule } from 'codecs/imagequant/imagequant';
import wasmUrl from 'url:codecs/imagequant/imagequant.wasm';
import { initEmscriptenModule } from 'features/util';
export interface QuantizeOptions {
zx: number;
maxNumColors: number;
dither: number;
}
import { Options } from '../shared/meta';
let emscriptenModule: Promise<QuantizerModule>;
export default async function process(
data: ImageData,
opts: QuantizeOptions,
opts: Options,
): Promise<ImageData> {
if (!emscriptenModule) {
emscriptenModule = initEmscriptenModule(imagequant, wasmUrl);

View File

@@ -3,5 +3,5 @@
"compilerOptions": {
"lib": ["webworker", "esnext"]
},
"references": [{ "path": "../../../" }]
"references": [{ "path": "../../../" }, { "path": "../shared" }]
}

View File

@@ -3,7 +3,7 @@ import {
BuiltinResizeMethod,
drawableToImageData,
} from 'client/util';
import { BrowserResizeOptions, VectorResizeOptions } from '../shared';
import { BrowserResizeOptions, VectorResizeOptions } from '../shared/meta';
import { getContainOffsets } from '../shared/util';
export function browserResize(

View File

@@ -23,7 +23,7 @@ type WorkerResizeMethods =
| 'lanczos3'
| 'hqx';
export type ResizeOptions =
export type Options =
| BrowserResizeOptions
| WorkerResizeOptions
| VectorResizeOptions;
@@ -47,3 +47,15 @@ export interface WorkerResizeOptions extends ResizeOptionsCommon {
export interface VectorResizeOptions extends ResizeOptionsCommon {
method: 'vector';
}
export const defaultOptions: Options = {
// Width and height will always default to the image size.
// This is set elsewhere.
width: 1,
height: 1,
// This will be set to 'vector' if the input is SVG.
method: 'lanczos3',
fitMethod: 'stretch',
premultiply: true,
linearRGB: true,
};

View File

@@ -1,4 +1,4 @@
import type { WorkerResizeOptions } from '../shared';
import type { WorkerResizeOptions } from '../shared/meta';
import { getContainOffsets } from '../shared/util';
import initResizeWasm, { resize as wasmResize } from 'codecs/resize/pkg';
import resizeWasmUrl from 'url:codecs/resize/pkg/squoosh_resize_bg.wasm';