diff --git a/README.md b/README.md index 203703d3..f60b0935 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,42 @@ # [Squoosh]! -[Squoosh] is an image compression web app that allows you to dive into the advanced options provided -by various image compressors. +[Squoosh] is an image compression web app that reduces image sizes through numerous formats. # API & CLI -Squoosh now has [an API](https://github.com/GoogleChromeLabs/squoosh/tree/dev/libsquoosh) and [a CLI](https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli) that allows you to compress many images at once. +Squoosh has [an API](https://github.com/GoogleChromeLabs/squoosh/tree/dev/libsquoosh) and [a CLI](https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli) to compress many images at once. # Privacy -Google Analytics is used to record the following: +Squoosh does not send your image to a server. All image compression processes locally. -- [Basic visit data](https://support.google.com/analytics/answer/6004245?ref_topic=2919631). -- Before and after image size once an image is downloaded. These values are rounded to the nearest - kilobyte. -- If install is available, when Squoosh is installed, and what method was used to install Squoosh. +However, Squoosh utilizes Google Analytics to collect the following: -Image compression is handled locally; no additional data is sent to the server. +- [Basic visitor data](https://support.google.com/analytics/answer/6004245?ref_topic=2919631). +- The before and after image size value. +- If Squoosh PWA, the type of Squoosh installation. +- If Squoosh PWA, the installation time and date. -# Building locally +# Developing -Clone the repo, and: +To develop for Squoosh: -```sh -npm install -npm run build -``` +1. Clone the repository +1. To install node packages, run: + ```sh + npm install + ``` +1. Then build the app by running: + ```sh + npm run build + ``` +1. After building, start the development server by running: + ```sh + npm run dev + ``` -You can run the development server with: +# Contributing -```sh -npm run dev -``` +Squoosh is an open-source project that appreciates all community involvement. To contribute to the project, follow the [contribute guide](/CONTRIBUTING.md). [squoosh]: https://squoosh.app diff --git a/libsquoosh/README.md b/libsquoosh/README.md index 3c11e010..af8c9a93 100644 --- a/libsquoosh/README.md +++ b/libsquoosh/README.md @@ -42,11 +42,19 @@ When an image has been ingested, you can start preprocessing it and encoding it await image.decoded; //Wait until the image is decoded before running preprocessors. const preprocessOptions = { + //When both width and height are specified, the image resized to specified size. resize: { enabled: true, width: 100, height: 50, } + /* + //When either width or height is specified, the image resized to specified size keeping aspect ratio. + resize: { + enabled: true, + width: 100, + } + */ } await image.preprocess(preprocessOptions); diff --git a/libsquoosh/src/auto-optimizer.js b/libsquoosh/src/auto-optimizer.ts similarity index 54% rename from libsquoosh/src/auto-optimizer.js rename to libsquoosh/src/auto-optimizer.ts index 1c536a3d..bffa4dc3 100644 --- a/libsquoosh/src/auto-optimizer.js +++ b/libsquoosh/src/auto-optimizer.ts @@ -2,6 +2,39 @@ import { instantiateEmscriptenWasm } from './emscripten-utils.js'; import visdif from '../../codecs/visdif/visdif.js'; import visdifWasm from 'asset-url:../../codecs/visdif/visdif.wasm'; +import type ImageData from './image_data'; + +interface VisDiff { + distance: (data: Uint8ClampedArray) => number; + delete: () => void; +} + +interface VisdiffConstructor { + new (data: Uint8ClampedArray, width: number, height: number): VisDiff; +} + +interface VisDiffModule extends EmscriptenWasm.Module { + VisDiff: VisdiffConstructor; +} + +type VisDiffModuleFactory = EmscriptenWasm.ModuleFactory; + +interface BinarySearchParams { + min?: number; + max?: number; + epsilon?: number; + maxRounds?: number; +} + +interface AutoOptimizeParams extends BinarySearchParams { + butteraugliDistanceGoal?: number; +} + +interface AutoOptimizeResult { + bitmap: ImageData; + binary: Uint8Array; + quality: number; +} // `measure` is a (async) function that takes exactly one numeric parameter and // returns a value. The function is assumed to be monotonic (an increase in `parameter` @@ -9,9 +42,9 @@ import visdifWasm from 'asset-url:../../codecs/visdif/visdif.wasm'; // to find `parameter` such that `measure` returns `measureGoal`, within an error // of `epsilon`. It will use at most `maxRounds` attempts. export async function binarySearch( - measureGoal, - measure, - { min = 0, max = 100, epsilon = 0.1, maxRounds = 6 } = {}, + measureGoal: number, + measure: (val: number) => Promise, + { min = 0, max = 100, epsilon = 0.1, maxRounds = 6 }: BinarySearchParams = {}, ) { let parameter = (max - min) / 2 + min; let delta = (max - min) / 4; @@ -33,12 +66,21 @@ export async function binarySearch( } export async function autoOptimize( - bitmapIn, - encode, - decode, - { butteraugliDistanceGoal = 1.4, ...otherOpts } = {}, -) { - const { VisDiff } = await instantiateEmscriptenWasm(visdif, visdifWasm); + bitmapIn: ImageData, + encode: ( + bitmap: ImageData, + quality: number, + ) => Promise | Uint8Array, + decode: (binary: Uint8Array) => Promise | ImageData, + { + butteraugliDistanceGoal = 1.4, + ...binarySearchParams + }: AutoOptimizeParams = {}, +): Promise { + const { VisDiff } = await instantiateEmscriptenWasm( + visdif as VisDiffModuleFactory, + visdifWasm, + ); const comparator = new VisDiff( bitmapIn.data, @@ -46,8 +88,11 @@ export async function autoOptimize( bitmapIn.height, ); - let bitmapOut; - let binaryOut; + // We're able to do non null assertion because + // we know that binarySearch will set these values + let bitmapOut!: ImageData; + let binaryOut!: Uint8Array; + // Increasing quality means _decrease_ in Butteraugli distance. // `binarySearch` assumes that increasing `parameter` will // increase the metric value. So multipliy Butteraugli values by -1. @@ -58,7 +103,7 @@ export async function autoOptimize( bitmapOut = await decode(binaryOut); return -1 * comparator.distance(bitmapOut.data); }, - otherOpts, + binarySearchParams, ); comparator.delete(); diff --git a/libsquoosh/src/worker_pool.js b/libsquoosh/src/worker_pool.js index 45f85afe..646d3870 100644 --- a/libsquoosh/src/worker_pool.js +++ b/libsquoosh/src/worker_pool.js @@ -64,7 +64,7 @@ export default class WorkerPool { async _nextWorker() { const reader = this.workerQueue.readable.getReader(); - const { value, done } = await reader.read(); + const { value } = await reader.read(); reader.releaseLock(); return value; } diff --git a/src/client/lazy-app/Compress/Options/Range/index.tsx b/src/client/lazy-app/Compress/Options/Range/index.tsx index da6e37e2..8b1150e3 100644 --- a/src/client/lazy-app/Compress/Options/Range/index.tsx +++ b/src/client/lazy-app/Compress/Options/Range/index.tsx @@ -6,10 +6,13 @@ import './custom-els/RangeInput'; import { linkRef } from 'shared/prerendered-app/util'; interface Props extends preact.JSX.HTMLAttributes {} -interface State {} +interface State { + textFocused: boolean; +} export default class Range extends Component { rangeWc?: RangeInputElement; + inputEl?: HTMLInputElement; private onTextInput = (event: Event) => { const input = event.target as HTMLInputElement; @@ -23,10 +26,19 @@ export default class Range extends Component { ); }; - render(props: Props) { + private onTextFocus = () => { + this.setState({ textFocused: true }); + }; + + private onTextBlur = () => { + this.setState({ textFocused: false }); + }; + + render(props: Props, state: State) { const { children, ...otherProps } = props; const { value, min, max, step } = props; + const textValue = state.textFocused ? this.inputEl!.value : value; return ( );