Compare commits

...

12 Commits

Author SHA1 Message Date
731ceba801 Bump json5 from 2.2.0 to 2.2.2 in /cli
Bumps [json5](https://github.com/json5/json5) from 2.2.0 to 2.2.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.0...v2.2.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-03 13:56:10 +00:00
918c596cba Add no-longer-maintained message to cli and libsquoosh 2023-01-03 13:55:36 +00:00
42594277fd Remove web codecs usage
It was decoding transparency incorrectly (multiplication wrong?), and I'd rather not have a different code path there for the sake of it
2022-12-15 14:56:51 +00:00
97d13de273 Add desktop screenshots to web app manifest (#1283)
Co-authored-by: jakearchibald <jaffathecake@gmail.com>
2022-09-18 03:22:10 +01:00
a1c3304c56 Fix issue with Chrome 105 and flexbox. Fixes #1281
https://bugs.chromium.org/p/chromium/issues/detail?id=1358807
2022-09-01 08:50:43 +01:00
7a2fc917c1 Remove margin in Safari between buttons in button group (#1275) 2022-08-18 12:45:10 +01:00
1c4233b5ae Add uniform spacing vertically and horizontally (#1277) 2022-08-18 12:44:11 +01:00
4d67ec5a7b Fix misplaced border (#1273) 2022-08-15 08:38:36 +01:00
c24505230d Bump version due to breaking changes 2022-02-18 15:36:31 +00:00
2238d1abaf Update version 2022-02-16 16:23:33 +00:00
65ea02627b Expose Typescript types in libSquoosh (#1142)
* Expose type declarations in libSquoosh npm package

* Add comment on why we remove the tsbuildinfo

* Fix PreprocessOptions type

Resize should require at least one of the width, height.
The other options are optional for all preprocessors

* Update libSquoosh README to reflect encode changes

I also removed requiring `await image.decoded` call before calling
preprocess or encode since they decode the image before the operation
2022-02-16 16:19:42 +00:00
18b53e2b8a JPEG XL: Add lossy Modular option (#1136)
* Improve .gitattributes

* Add disabled checkbox style

* Update Makefile

* Update jxl_enc.cpp

* add -O3 flag to skia compilation for optimization's sake

* Bump libjxl revision to 9f544641ec83f6abd9da598bdd08178ee8a003e0

Change use of EncodeFile from `EncodeFile(cparams, &io, &passes_enc_state, &bytes, /*aux=*/nullptr, pool_ptr)` to `EncodeFile(cparams, &io, &passes_enc_state, &bytes, /*cms=*/nullptr, /*aux=*/jxl::GetJxlCms(), pool_ptr)`

* JPEG XL: Add lossy Modular option

Co-authored-by: CanadianBaconBoi <beamconnor@gmail.com>
Co-authored-by: CanadianBaconBoi <bc.bacon.bits@gmail.com>
2022-01-21 12:42:27 +00:00
41 changed files with 146 additions and 213 deletions

4
.gitattributes vendored
View File

@ -1,2 +1,2 @@
/codecs/**/*.js linguist-generated=true
/codecs/*/pkg*/*.d.ts linguist-generated=true
/codecs/**/*.js linguist-generated -diff
/codecs/*/pkg*/*.d.ts linguist-generated

View File

@ -1,3 +1,7 @@
# Project no longer maintained
Unfortunately, due to a few people leaving the team, and staffing issues resulting from the current economic climate (ugh), this package is no longer actively maintained. I know that sucks, but there simply isn't the time & people to work on this. If anyone from the community wants to fork it, you have my blessing. The [squoosh.app](https://squoosh.app) will continue to be supported and improved.
# Squoosh CLI
Squoosh CLI is an _experimental_ way to run all the codecs you know from the [Squoosh] web app on your command line using WebAssembly. The Squoosh CLI uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.

32
cli/package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@squoosh/cli",
"version": "0.7.2",
"version": "0.7.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@squoosh/cli",
"version": "0.7.2",
"version": "0.7.3",
"license": "Apache-2.0",
"dependencies": {
"@squoosh/lib": "^0.4.0",
@ -184,12 +184,9 @@
}
},
"node_modules/json5": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
"integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
"dependencies": {
"minimist": "^1.2.5"
},
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz",
"integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==",
"bin": {
"json5": "lib/cli.js"
},
@ -225,11 +222,6 @@
"node": ">=6"
}
},
"node_modules/minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"node_modules/onetime": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
@ -478,12 +470,9 @@
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="
},
"json5": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
"integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
"requires": {
"minimist": "^1.2.5"
}
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz",
"integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ=="
},
"kleur": {
"version": "4.1.4",
@ -504,11 +493,6 @@
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"onetime": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "@squoosh/cli",
"version": "0.7.2",
"version": "0.7.3",
"description": "A CLI for Squoosh",
"public": true,
"type": "module",

View File

@ -1,5 +1,5 @@
CODEC_URL = https://github.com/libjxl/libjxl.git
CODEC_VERSION = v0.5
CODEC_VERSION = 9f544641ec83f6abd9da598bdd08178ee8a003e0
CODEC_DIR = node_modules/jxl
CODEC_BUILD_ROOT := $(CODEC_DIR)/build
CODEC_MT_BUILD_DIR := $(CODEC_BUILD_ROOT)/mt
@ -75,6 +75,9 @@ $(CODEC_MT_SIMD_BUILD_DIR)/Makefile: CXXFLAGS+=-msimd128
-DCMAKE_CROSSCOMPILING_EMULATOR=node \
-B $(@D) \
$(<D)
emcc -Wall -O3 -o $(CODEC_DIR)/third_party/skcms/skcms.cc.o -I$(CODEC_DIR)/third_party/skcms -c $(CODEC_DIR)/third_party/skcms/skcms.cc
llvm-ar rc $(CODEC_BUILD_DIR)/third_party/libskcms.a $(CODEC_DIR)/third_party/skcms/skcms.cc.o
rm $(CODEC_DIR)/third_party/skcms/skcms.cc.o
$(CODEC_DIR)/CMakeLists.txt:
$(RM) -r $(@D)

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -4,6 +4,7 @@
#include "lib/jxl/base/thread_pool_internal.h"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/enc_file.h"
#include "lib/jxl/enc_color_management.h"
using namespace emscripten;
@ -17,6 +18,7 @@ struct JXLOptions {
bool lossyPalette;
size_t decodingSpeedTier;
float photonNoiseIso;
bool lossyModular;
};
val encode(std::string image, int width, int height, JXLOptions options) {
@ -50,11 +52,16 @@ val encode(std::string image, int width, int height, JXLOptions options) {
float quality = options.quality;
// Quality settings roughly match libjpeg qualities.
if (quality < 7 || quality == 100) {
if (options.lossyModular || quality == 100) {
cparams.modular_mode = true;
// Internal modular quality to roughly match VarDCT size.
cparams.quality_pair.first = cparams.quality_pair.second =
std::min(35 + (quality - 7) * 3.0f, 100.0f);
if (quality < 7) {
cparams.quality_pair.first = cparams.quality_pair.second =
std::min(35 + (quality - 7) * 3.0f, 100.0f);
} else {
cparams.quality_pair.first = cparams.quality_pair.second =
std::min(35 + (quality - 7) * 65.f / 93.f, 100.0f);
}
} else {
cparams.modular_mode = false;
if (quality >= 30) {
@ -91,14 +98,14 @@ val encode(std::string image, int width, int height, JXLOptions options) {
jxl::Span<const uint8_t>(reinterpret_cast<const uint8_t*>(image.data()), image.size()), width,
height, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/8, /*endiannes=*/JXL_LITTLE_ENDIAN,
/*flipped_y=*/false, pool_ptr, main);
/*flipped_y=*/false, pool_ptr, main, /*(only true if bits_per_sample==32) float_in=*/false);
if (!result) {
return val::null();
}
auto js_result = val::null();
if (EncodeFile(cparams, &io, &passes_enc_state, &bytes, /*aux=*/nullptr, pool_ptr)) {
if (EncodeFile(cparams, &io, &passes_enc_state, &bytes, jxl::GetJxlCms(), /*aux=*/nullptr, pool_ptr)) {
js_result = Uint8Array.new_(typed_memory_view(bytes.size(), bytes.data()));
}
@ -113,6 +120,7 @@ EMSCRIPTEN_BINDINGS(my_module) {
.field("lossyPalette", &JXLOptions::lossyPalette)
.field("decodingSpeedTier", &JXLOptions::decodingSpeedTier)
.field("photonNoiseIso", &JXLOptions::photonNoiseIso)
.field("lossyModular", &JXLOptions::lossyModular)
.field("epf", &JXLOptions::epf);
function("encode", &encode);

View File

@ -6,6 +6,7 @@ export interface EncodeOptions {
lossyPalette: boolean;
decodingSpeedTier: number;
photonNoiseIso: number;
lossyModular: boolean;
}
export interface JXLModule extends EmscriptenWasm.Module {

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -1,3 +1,7 @@
# Project no longer maintained
Unfortunately, due to a few people leaving the team, and staffing issues resulting from the current economic climate (ugh), this package is no longer actively maintained. I know that sucks, but there simply isn't the time & people to work on this. If anyone from the community wants to fork it, you have my blessing. The [squoosh.app](https://squoosh.app) will continue to be supported and improved.
# libSquoosh
libSquoosh is an _experimental_ way to run all the codecs you know from the [Squoosh] web app directly inside your own JavaScript program. libSquoosh uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
@ -43,19 +47,15 @@ The returned `image` object is a representation of the original image, that you
When an image has been ingested, you can start preprocessing it and encoding it to other formats. This example will resize the image and then encode it to a `.jpg` and `.jxl` image:
```js
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,
}
*/
@ -68,7 +68,7 @@ const encodeOptions = {
quality: 90,
},
};
await image.encode(encodeOptions);
const result = await image.encode(encodeOptions);
```
The default values for each option can be found in the [`codecs.ts`][codecs.ts] file under `defaultEncoderOptions`. Every unspecified value will use the default value specified there. _Better documentation is needed here._
@ -92,7 +92,7 @@ When you have encoded an image, you normally want to write it to a file.
This example takes an image that has been encoded as a `jpg` and writes it to a file:
```js
const rawEncodedImage = (await image.encodedWith.mozjpeg).binary;
const rawEncodedImage = image.encodedWith.mozjpeg.binary;
fs.writeFile('/path/to/new/image.jpg', rawEncodedImage);
```
@ -103,10 +103,7 @@ This example iterates through all encoded versions of the image and writes them
const newImagePath = '/path/to/image.'; //extension is added automatically
for (const encodedImage of Object.values(image.encodedWith)) {
fs.writeFile(
newImagePath + (await encodedImage).extension,
(await encodedImage).binary,
);
fs.writeFile(newImagePath + encodedImage.extension, encodedImage.binary);
}
```
@ -135,7 +132,7 @@ console.log(await image.decoded);
Information about an encoded image can be found at `Image.encodedWith[encoderName]`. It looks something like this:
```js
console.log(await image.encodedWith.jxl);
console.log(image.encodedWith.jxl);
// Returns:
{
optionsUsed: {

View File

@ -0,0 +1,26 @@
const fs = require('fs');
const del = require('del');
const path = require('path');
const tsOutputDir = path.resolve('..', '.tmp', 'ts', 'libsquoosh');
const tsOutputSourceDir = path.join(tsOutputDir, 'src');
const buildDir = path.resolve('build');
(async () => {
await del(path.join(buildDir, '*.d.ts'));
const files = await fs.promises.readdir(tsOutputSourceDir);
const movePromises = [];
for (const file of files) {
if (file.endsWith('d.ts') || file.endsWith('d.ts.map')) {
movePromises.push(
fs.promises.rename(
path.join(tsOutputSourceDir, file),
path.join(buildDir, file),
),
);
}
}
// We need to remove `tsconfig.tsbuildinfo` otherwise TS does not emit unchanged `.d.ts` files
await del([path.join(tsOutputDir, 'tsconfig.tsbuildinfo')], { force: true });
})();

View File

@ -1,12 +1,12 @@
{
"name": "@squoosh/lib",
"version": "0.4.0",
"version": "0.5.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@squoosh/lib",
"version": "0.4.0",
"version": "0.5.2",
"license": "Apache-2.0",
"dependencies": {
"wasm-feature-detect": "^1.2.11",

View File

@ -1,14 +1,15 @@
{
"name": "@squoosh/lib",
"version": "0.4.0",
"version": "0.5.2",
"description": "A Node library for Squoosh",
"public": true,
"main": "./build/index.js",
"types": "./build/index.d.ts",
"files": [
"/build/*"
],
"scripts": {
"build": "rollup -c"
"build": "rollup -c && node lib/move-d-ts.js"
},
"keywords": [],
"author": "Google Chrome Developers <chromium-dev@google.com>",

View File

@ -419,6 +419,7 @@ export const codecs = {
lossyPalette: false,
decodingSpeedTier: 0,
photonNoiseIso: 0,
lossyModular: false,
},
autoOptimize: {
option: 'quality',

View File

@ -22,9 +22,10 @@ type EncoderKey = keyof typeof encoders;
type PreprocessorKey = keyof typeof preprocessors;
type PreprocessOptions = {
resize?: ResizeOptions;
quant?: QuantOptions;
rotate?: RotateOptions;
resize?: Partial<Omit<ResizeOptions, 'width' | 'height'>> &
(Pick<ResizeOptions, 'width'> | Pick<ResizeOptions, 'height'>);
quant?: Partial<QuantOptions>;
rotate?: Partial<RotateOptions>;
};
type EncodeResult = {
optionsUsed: object;

View File

@ -3,7 +3,8 @@
"compilerOptions": {
"lib": ["esnext"],
"types": ["node"],
"allowJs": true
"allowJs": true,
"declaration": true
},
"include": ["src/**/*", "../codecs/**/*"]
}

View File

@ -11,7 +11,11 @@ export default class Checkbox extends Component<Props, State> {
return (
<div class={style.checkbox}>
{props.checked ? (
<CheckedIcon class={`${style.icon} ${style.checked}`} />
props.disabled ? (
<CheckedIcon class={`${style.icon} ${style.disabled}`} />
) : (
<CheckedIcon class={`${style.icon} ${style.checked}`} />
)
) : (
<UncheckedIcon class={style.icon} />
)}

View File

@ -20,3 +20,7 @@
.checked {
fill: var(--main-theme-color);
}
.disabled {
fill: var(--dark-gray);
}

View File

@ -40,7 +40,8 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
}
.scrubber {
display: flex;
display: grid;
align-content: center;
position: absolute;
top: 50%;
left: 50%;
@ -54,10 +55,6 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
padding: 0 calc(var(--thumb-size) * 0.24);
}
.scrubber svg {
flex: 1;
}
.arrow-left {
fill: var(--pink);
}

View File

@ -49,6 +49,7 @@
align-self: center;
padding: 9px 66px;
position: relative;
gap: 6px;
/* Allow clicks to fall through to the pinch zoom area */
pointer-events: none;
@ -68,7 +69,6 @@
display: flex;
position: relative;
z-index: 100;
margin: 0 3px;
}
.button,
@ -95,6 +95,7 @@
.button {
color: #fff;
margin: 0;
&:hover {
background: rgba(50, 50, 50, 0.92);
@ -114,7 +115,7 @@
.last-button {
composes: button;
border-radius: 0 6px 6px 0;
border-left-width: 1px;
border-right-width: 1px;
}
.zoom {

View File

@ -114,7 +114,7 @@ async function decodeImage(
}
}
// Otherwise fall through and try built-in decoding for a laugh.
return await builtinDecode(signal, blob, mimeType);
return await builtinDecode(signal, blob);
} catch (err) {
if (err instanceof Error && err.name === 'AbortError') throw err;
console.log(err);

View File

@ -63,35 +63,17 @@ interface DrawableToImageDataOptions {
sh?: number;
}
function getWidth(
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
): number {
if ('displayWidth' in drawable) {
return drawable.displayWidth;
}
return drawable.width;
}
function getHeight(
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
): number {
if ('displayHeight' in drawable) {
return drawable.displayHeight;
}
return drawable.height;
}
export function drawableToImageData(
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
drawable: ImageBitmap | HTMLImageElement,
opts: DrawableToImageDataOptions = {},
): ImageData {
const {
width = getWidth(drawable),
height = getHeight(drawable),
width = drawable.width,
height = drawable.height,
sx = 0,
sy = 0,
sw = getWidth(drawable),
sh = getHeight(drawable),
sw = drawable.width,
sh = drawable.height,
} = opts;
// Make canvas same size as image

View File

@ -10,8 +10,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as WebCodecs from '../util/web-codecs';
import { drawableToImageData } from './canvas';
/** If render engine is Safari */
@ -139,15 +137,7 @@ export async function blobToImg(blob: Blob): Promise<HTMLImageElement> {
export async function builtinDecode(
signal: AbortSignal,
blob: Blob,
mimeType: string,
): Promise<ImageData> {
// If WebCodecs are supported, use that.
if (await WebCodecs.isTypeSupported(mimeType)) {
assertSignal(signal);
try {
return await abortable(signal, WebCodecs.decode(blob, mimeType));
} catch (e) {}
}
assertSignal(signal);
// Prefer createImageBitmap as it's the off-thread option for Firefox.

View File

@ -1,33 +0,0 @@
import { drawableToImageData } from '../canvas';
const hasImageDecoder = typeof ImageDecoder !== 'undefined';
export async function isTypeSupported(mimeType: string): Promise<boolean> {
if (!hasImageDecoder) return false;
// Some old versions of this API threw here.
// It only impacted folks with experimental web platform flags enabled in Chrome 90.
// The API was updated in Chrome 91.
try {
return await ImageDecoder.isTypeSupported(mimeType);
} catch (err) {
return false;
}
}
export async function decode(
blob: Blob | File,
mimeType: string,
): Promise<ImageData> {
if (!hasImageDecoder) {
throw Error(
`This browser does not support ImageDecoder. This function should not have been called.`,
);
}
const decoder = new ImageDecoder({
type: mimeType,
// Non-obvious way to turn an Blob into a ReadableStream
data: new Response(blob).body!,
});
const { image } = await decoder.decode();
return drawableToImageData(image);
}

View File

@ -1,60 +0,0 @@
interface ImageDecoderInit {
type: string;
data: BufferSource | ReadableStream;
premultiplyAlpha?: PremultiplyAlpha;
colorSpaceConversion?: ColorSpaceConversion;
desiredWidth?: number;
desiredHeight?: number;
preferAnimation?: boolean;
}
interface ImageDecodeOptions {
frameIndex: number;
completeFramesOnly: boolean;
}
interface ImageDecodeResult {
image: VideoFrame;
complete: boolean;
}
// I didnt do all the types because the class is kinda complex.
// I focused on what we need.
// See https://w3c.github.io/webcodecs/#videoframe
declare class VideoFrame {
displayWidth: number;
displayHeight: number;
}
// Add VideoFrame to canvas drawImage()
interface CanvasDrawImage {
drawImage(
image: CanvasImageSource | VideoFrame,
dx: number,
dy: number,
): void;
drawImage(
image: CanvasImageSource | VideoFrame,
dx: number,
dy: number,
dw: number,
dh: number,
): void;
drawImage(
image: CanvasImageSource | VideoFrame,
sx: number,
sy: number,
sw: number,
sh: number,
dx: number,
dy: number,
dw: number,
dh: number,
): void;
}
declare class ImageDecoder {
static isTypeSupported(type: string): Promise<boolean>;
constructor(desc: ImageDecoderInit);
decode(opts?: Partial<ImageDecodeOptions>): Promise<ImageDecodeResult>;
}

View File

@ -30,6 +30,7 @@ interface State {
autoEdgePreservingFilter: boolean;
decodingSpeedTier: number;
photonNoiseIso: number;
alternativeLossy: boolean;
}
export class Options extends Component<Props, State> {
@ -55,6 +56,7 @@ export class Options extends Component<Props, State> {
autoEdgePreservingFilter: options.epf === -1,
decodingSpeedTier: options.decodingSpeedTier,
photonNoiseIso: options.photonNoiseIso,
alternativeLossy: options.lossyModular,
};
}
@ -96,6 +98,7 @@ export class Options extends Component<Props, State> {
lossyPalette: optionState.lossless ? optionState.slightLoss : false,
decodingSpeedTier: optionState.decodingSpeedTier,
photonNoiseIso: optionState.photonNoiseIso,
lossyModular: optionState.quality < 7 ? true : optionState.alternativeLossy,
};
// Updating options, so we don't recalculate in getDerivedStateFromProps.
@ -122,6 +125,7 @@ export class Options extends Component<Props, State> {
autoEdgePreservingFilter,
decodingSpeedTier,
photonNoiseIso,
alternativeLossy,
}: State,
) {
// I'm rendering both lossy and lossless forms, as it becomes much easier when
@ -162,6 +166,14 @@ export class Options extends Component<Props, State> {
Quality:
</Range>
</div>
<label class={style.optionToggle}>
Alternative lossy mode
<Checkbox
checked={quality < 7 ? true : alternativeLossy}
disabled={quality < 7}
onChange={this._inputChange('alternativeLossy', 'boolean')}
/>
</label>
<label class={style.optionToggle}>
Auto edge filter
<Checkbox
@ -223,7 +235,7 @@ export class Options extends Component<Props, State> {
</label>
<div class={style.optionOneCell}>
<Range
min="3"
min="1"
max="9"
value={effort}
onInput={this._inputChange('effort', 'number')}

View File

@ -25,4 +25,5 @@ export const defaultOptions: EncodeOptions = {
lossyPalette: false,
decodingSpeedTier: 0,
photonNoiseIso: 0,
lossyModular: false,
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -19,15 +19,39 @@ import * as iconLarge from 'img-url:static-build/assets/icon-large.png';
import * as screenshot1 from 'img-url:static-build/assets/screenshot1.png';
import * as screenshot2 from 'img-url:static-build/assets/screenshot2.jpg';
import * as screenshot3 from 'img-url:static-build/assets/screenshot3.jpg';
import * as screenshot4 from 'img-url:static-build/assets/screenshot4.png';
import * as screenshot5 from 'img-url:static-build/assets/screenshot5.jpg';
import * as screenshot6 from 'img-url:static-build/assets/screenshot6.jpg';
import dedent from 'dedent';
import { lookup as lookupMime } from 'mime-types';
const manifestSize = ({ width, height }: { width: number; height: number }) =>
`${width}x${height}`;
interface Dimensions {
width: number;
height: number;
}
const manifestSize = ({ width, height }: Dimensions) => `${width}x${height}`;
const formFactor = ({ width, height }: Dimensions) =>
width > height ? 'wide' : 'narrow';
const screenshots = [
screenshot1,
screenshot2,
screenshot3,
screenshot4,
screenshot5,
screenshot6,
].map((screenshot) => ({
src: screenshot.default,
type: lookupMime(screenshot.default),
sizes: manifestSize(screenshot),
form_factor: formFactor(screenshot),
}));
interface Output {
[outputPath: string]: string;
}
const toOutput: Output = {
'index.html': renderPage(<IndexPage />),
'manifest.json': JSON.stringify({
@ -55,23 +79,7 @@ const toOutput: Output = {
'Compress and compare images with different codecs, right in your browser.',
lang: 'en',
categories: ['photo', 'productivity', 'utilities'],
screenshots: [
{
src: screenshot1.default,
type: lookupMime(screenshot1.default),
sizes: manifestSize(screenshot1),
},
{
src: screenshot2.default,
type: lookupMime(screenshot2.default),
sizes: manifestSize(screenshot2),
},
{
src: screenshot3.default,
type: lookupMime(screenshot3.default),
sizes: manifestSize(screenshot3),
},
],
screenshots,
share_target: {
action: '/?utm_medium=PWA&utm_source=share-target&share-target',
method: 'POST',