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 () { export default function () {
let previousWorkerImports; let previousWorkerImports;
let previousEncoderMetas; let previousJoinedMetas;
/** /**
* Generates the worker file & tsconfig for all features * Generates the worker file & tsconfig for all features
@@ -134,7 +134,7 @@ export default function () {
const joinedWorkerImports = workerImports.join(); const joinedWorkerImports = workerImports.join();
// Avoid regenerating if nothing's changed. // 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; if (joinedWorkerImports === previousWorkerImports) return;
previousWorkerImports = joinedWorkerImports; previousWorkerImports = joinedWorkerImports;
@@ -153,6 +153,14 @@ export default function () {
.filter((tsFile) => !tsFile.endsWith('.d.ts')) .filter((tsFile) => !tsFile.endsWith('.d.ts'))
.map((tsFile) => tsFile.slice(0, -'.ts'.length)); .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( const featureMetaBasePath = path.join(
process.cwd(), process.cwd(),
'src', 'src',
@@ -161,20 +169,24 @@ export default function () {
'feature-meta', 'feature-meta',
); );
const joinedEncoderMetas = encoderMetas.join(); const joinedMetas = [...encoderMetas, ...processorMetas].join();
// Avoid regenerating if nothing's changed. // 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 (joinedEncoderMetas === previousEncoderMetas) return; if (joinedMetas === previousJoinedMetas) return;
previousEncoderMetas = joinedEncoderMetas; previousJoinedMetas = joinedMetas;
const encoderMetaTsNames = encoderMetas.map((tsImport) => [ const getTsName = (tsImport) => [
path.relative(featureMetaBasePath, tsImport), path.relative(featureMetaBasePath, tsImport),
path.basename(tsImport.slice(0, -'/shared/meta'.length)), path.basename(tsImport.slice(0, -'/shared/meta'.length)),
]); ];
const encoderMetaTsNames = encoderMetas.map(getTsName);
const processorMetaTsNames = processorMetas.map(getTsName);
const featureMeta = [ const featureMeta = [
autoGenComment, autoGenComment,
// Encoder stuff
encoderMetaTsNames.map( encoderMetaTsNames.map(
([path, name]) => `import * as ${name}EncoderMeta from '${path}';`, ([path, name]) => `import * as ${name}EncoderMeta from '${path}';`,
), ),
@@ -194,6 +206,22 @@ export default function () {
`};`, `};`,
`export type EncoderType = keyof typeof encoderMap`, `export type EncoderType = keyof typeof encoderMap`,
`export const encoders = [...Object.values(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) .flat(Infinity)
.join('\n'); .join('\n');

View File

@@ -1,6 +1,5 @@
import { EncoderState } from '../feature-meta'; import { EncoderState, ProcessorState } from '../feature-meta';
import { shallowEqual } from '../../lib/util'; import { shallowEqual } from '../../util';
import { PreprocessorState } from '../../codecs/preprocessors';
import * as identity from '../../codecs/identity/encoder-meta'; import * as identity from '../../codecs/identity/encoder-meta';
@@ -11,7 +10,7 @@ interface CacheResult {
} }
interface CacheEntry extends CacheResult { interface CacheEntry extends CacheResult {
preprocessorState: PreprocessorState; processorState: ProcessorState;
encoderState: EncoderState; encoderState: EncoderState;
sourceData: ImageData; sourceData: ImageData;
} }
@@ -32,7 +31,7 @@ export default class ResultCache {
match( match(
sourceData: ImageData, sourceData: ImageData,
preprocessorState: PreprocessorState, processorState: ProcessorState,
encoderState: EncoderState, encoderState: EncoderState,
): CacheResult | undefined { ): CacheResult | undefined {
const matchingIndex = this._entries.findIndex((entry) => { const matchingIndex = this._entries.findIndex((entry) => {
@@ -41,11 +40,11 @@ export default class ResultCache {
if (entry.encoderState.type !== encoderState.type) return false; if (entry.encoderState.type !== encoderState.type) return false;
// Check that each set of options in the preprocessor are the same // Check that each set of options in the preprocessor are the same
for (const prop in preprocessorState) { for (const prop in processorState) {
if ( if (
!shallowEqual( !shallowEqual(
(preprocessorState as any)[prop], (processorState as any)[prop],
(entry.preprocessorState as any)[prop], (entry.processorState as any)[prop],
) )
) )
return false; 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 imagequant, { QuantizerModule } from 'codecs/imagequant/imagequant';
import wasmUrl from 'url:codecs/imagequant/imagequant.wasm'; import wasmUrl from 'url:codecs/imagequant/imagequant.wasm';
import { initEmscriptenModule } from 'features/util'; import { initEmscriptenModule } from 'features/util';
import { Options } from '../shared/meta';
export interface QuantizeOptions {
zx: number;
maxNumColors: number;
dither: number;
}
let emscriptenModule: Promise<QuantizerModule>; let emscriptenModule: Promise<QuantizerModule>;
export default async function process( export default async function process(
data: ImageData, data: ImageData,
opts: QuantizeOptions, opts: Options,
): Promise<ImageData> { ): Promise<ImageData> {
if (!emscriptenModule) { if (!emscriptenModule) {
emscriptenModule = initEmscriptenModule(imagequant, wasmUrl); emscriptenModule = initEmscriptenModule(imagequant, wasmUrl);

View File

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

View File

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

View File

@@ -23,7 +23,7 @@ type WorkerResizeMethods =
| 'lanczos3' | 'lanczos3'
| 'hqx'; | 'hqx';
export type ResizeOptions = export type Options =
| BrowserResizeOptions | BrowserResizeOptions
| WorkerResizeOptions | WorkerResizeOptions
| VectorResizeOptions; | VectorResizeOptions;
@@ -47,3 +47,15 @@ export interface WorkerResizeOptions extends ResizeOptionsCommon {
export interface VectorResizeOptions extends ResizeOptionsCommon { export interface VectorResizeOptions extends ResizeOptionsCommon {
method: 'vector'; 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 { getContainOffsets } from '../shared/util';
import initResizeWasm, { resize as wasmResize } from 'codecs/resize/pkg'; import initResizeWasm, { resize as wasmResize } from 'codecs/resize/pkg';
import resizeWasmUrl from 'url:codecs/resize/pkg/squoosh_resize_bg.wasm'; import resizeWasmUrl from 'url:codecs/resize/pkg/squoosh_resize_bg.wasm';