Merge branch 'v2-codecs' into jxl

This commit is contained in:
Surma
2020-08-17 15:18:23 +01:00
161 changed files with 3406 additions and 7582 deletions

View File

@@ -3,21 +3,23 @@ import { h, Component } from 'preact';
import * as style from './style.scss';
import { bind } from '../../lib/initial-util';
import { cleanSet, cleanMerge } from '../../lib/clean-modify';
import OptiPNGEncoderOptions from '../../codecs/optipng/options';
import OxiPNGEncoderOptions from '../../codecs/oxipng/options';
import MozJpegEncoderOptions from '../../codecs/mozjpeg/options';
import BrowserJPEGEncoderOptions from '../../codecs/browser-jpeg/options';
import WebPEncoderOptions from '../../codecs/webp/options';
import JXLEncoderOptions from '../../codecs/jxl/options';
import AvifEncoderOptions from '../../codecs/avif/options';
import BrowserWebPEncoderOptions from '../../codecs/browser-webp/options';
import QuantizerOptionsComponent from '../../codecs/imagequant/options';
import ResizeOptionsComponent from '../../codecs/resize/options';
import * as identity from '../../codecs/identity/encoder-meta';
import * as optiPNG from '../../codecs/optipng/encoder-meta';
import * as oxiPNG from '../../codecs/oxipng/encoder-meta';
import * as mozJPEG from '../../codecs/mozjpeg/encoder-meta';
import * as webP from '../../codecs/webp/encoder-meta';
import * as jxl from '../../codecs/jxl/encoder-meta';
import * as avif from '../../codecs/avif/encoder-meta';
import * as browserPNG from '../../codecs/browser-png/encoder-meta';
import * as browserJPEG from '../../codecs/browser-jpeg/encoder-meta';
import * as browserWebP from '../../codecs/browser-webp/encoder-meta';
@@ -46,10 +48,11 @@ const encoderOptionsComponentMap: {
[x: string]: (new (...args: any[]) => Component<any, any>) | undefined;
} = {
[identity.type]: undefined,
[optiPNG.type]: OptiPNGEncoderOptions,
[oxiPNG.type]: OxiPNGEncoderOptions,
[mozJPEG.type]: MozJpegEncoderOptions,
[webP.type]: WebPEncoderOptions,
[jxl.type]: JXLEncoderOptions,
[avif.type]: AvifEncoderOptions,
[browserPNG.type]: undefined,
[browserJPEG.type]: BrowserJPEGEncoderOptions,
[browserWebP.type]: BrowserWebPEncoderOptions,

View File

@@ -7,10 +7,11 @@ import Output from '../Output';
import Options from '../Options';
import ResultCache from './result-cache';
import * as identity from '../../codecs/identity/encoder-meta';
import * as optiPNG from '../../codecs/optipng/encoder-meta';
import * as oxiPNG from '../../codecs/oxipng/encoder-meta';
import * as mozJPEG from '../../codecs/mozjpeg/encoder-meta';
import * as webP from '../../codecs/webp/encoder-meta';
import * as jxl from '../../codecs/jxl/encoder-meta';
import * as avif from '../../codecs/avif/encoder-meta';
import * as browserPNG from '../../codecs/browser-png/encoder-meta';
import * as browserJPEG from '../../codecs/browser-jpeg/encoder-meta';
import * as browserWebP from '../../codecs/browser-webp/encoder-meta';
@@ -110,11 +111,6 @@ async function preprocessImage(
} else if (isHqx(preprocessData.resize)) {
// Hqx can only do x2, x3 or x4.
result = await processor.workerResize(result, preprocessData.resize);
// Seems like the globals from Rust from hqx and resize are conflicting.
// For now we can fix that by terminating the worker.
// TODO: Use wasm-bindgens new --web target to create a proper ES6 module
// and remove this.
processor.terminateWorker();
// If the target size is not a clean x2, x3 or x4, use Catmull-Rom
// for the remaining scaling.
const pixelOpts = { ...preprocessData.resize, method: 'catrom' };
@@ -139,10 +135,11 @@ async function compressImage(
): Promise<Fileish> {
const compressedData = await (() => {
switch (encodeData.type) {
case optiPNG.type: return processor.optiPngEncode(image, encodeData.options);
case oxiPNG.type: return processor.oxiPngEncode(image, encodeData.options);
case mozJPEG.type: return processor.mozjpegEncode(image, encodeData.options);
case webP.type: return processor.webpEncode(image, encodeData.options);
case jxl.type: return processor.jxlEncode(image, encodeData.options);
case avif.type: return processor.avifEncode(image, encodeData.options);
case browserPNG.type: return processor.browserPngEncode(image);
case browserJPEG.type: return processor.browserJpegEncode(image, encodeData.options);
case browserWebP.type: return processor.browserWebpEncode(image, encodeData.options);

View File

@@ -41,17 +41,31 @@ const demos = [
},
];
const installButtonSource = 'introInstallButton-Purple';
interface Props {
onFile: (file: File | Fileish) => void;
showSnack: SnackBarElement['showSnackbar'];
}
interface State {
fetchingDemoIndex?: number;
beforeInstallEvent?: BeforeInstallPromptEvent;
}
export default class Intro extends Component<Props, State> {
state: State = {};
private fileInput?: HTMLInputElement;
private installingViaButton = false;
constructor() {
super();
// Listen for beforeinstallprompt events, indicating Squoosh is installable.
window.addEventListener('beforeinstallprompt', this.onBeforeInstallPromptEvent);
// Listen for the appinstalled event, indicating Squoosh has been installed.
window.addEventListener('appinstalled', this.onAppInstalled);
}
@bind
private resetFileInput() {
@@ -90,7 +104,71 @@ export default class Intro extends Component<Props, State> {
}
}
render({ }: Props, { fetchingDemoIndex }: State) {
@bind
private onBeforeInstallPromptEvent(event: BeforeInstallPromptEvent) {
// Don't show the mini-infobar on mobile
event.preventDefault();
// Save the beforeinstallprompt event so it can be called later.
this.setState({ beforeInstallEvent: event });
// Log the event.
const gaEventInfo = {
eventCategory: 'pwa-install',
eventAction: 'promo-shown',
nonInteraction: true,
};
ga('send', 'event', gaEventInfo);
}
@bind
private async onInstallClick(event: Event) {
// Get the deferred beforeinstallprompt event
const beforeInstallEvent = this.state.beforeInstallEvent;
// If there's no deferred prompt, bail.
if (!beforeInstallEvent) return;
this.installingViaButton = true;
// Show the browser install prompt
beforeInstallEvent.prompt();
// Wait for the user to accept or dismiss the install prompt
const { outcome } = await beforeInstallEvent.userChoice;
// Send the analytics data
const gaEventInfo = {
eventCategory: 'pwa-install',
eventAction: 'promo-clicked',
eventLabel: installButtonSource,
eventValue: outcome === 'accepted' ? 1 : 0,
};
ga('send', 'event', gaEventInfo);
// If the prompt was dismissed, we aren't going to install via the button.
if (outcome === 'dismissed') {
this.installingViaButton = false;
}
}
@bind
private onAppInstalled() {
// We don't need the install button, if it's shown
this.setState({ beforeInstallEvent: undefined });
// Don't log analytics if page is not visible
if (document.hidden) {
return;
}
// Try to get the install, if it's not set, use 'browser'
const source = this.installingViaButton ? installButtonSource : 'browser';
ga('send', 'event', 'pwa-install', 'installed', source);
// Clear the install method property
this.installingViaButton = false;
}
render({ }: Props, { fetchingDemoIndex, beforeInstallEvent }: State) {
return (
<div class={style.intro}>
<div>
@@ -120,7 +198,7 @@ export default class Intro extends Component<Props, State> {
<img class={style.demoIcon} src={demo.iconUrl} alt="" decoding="async" />
{fetchingDemoIndex === i &&
<div class={style.demoLoading}>
<loading-spinner class={style.demoLoadingSpinner}/>
<loading-spinner class={style.demoLoadingSpinner} />
</div>
}
</div>
@@ -132,11 +210,20 @@ export default class Intro extends Component<Props, State> {
)}
</ul>
</div>
{beforeInstallEvent &&
<button
type="button"
class={style.installButton}
onClick={this.onInstallClick}
>
Install
</button>
}
<ul class={style.relatedLinks}>
<li><a href="https://github.com/GoogleChromeLabs/squoosh/">View the code</a></li>
<li><a href="https://github.com/GoogleChromeLabs/squoosh/issues">Report a bug</a></li>
<li>
<a href="https://github.com/GoogleChromeLabs/squoosh/blob/master/README.md#privacy">
<a href="https://github.com/GoogleChromeLabs/squoosh/blob/dev/README.md#privacy">
Privacy
</a>
</li>

32
src/components/intro/missing-types.d.ts vendored Normal file
View File

@@ -0,0 +1,32 @@
/**
* The BeforeInstallPromptEvent is fired at the Window.onbeforeinstallprompt handler
* before a user is prompted to "install" a web site to a home screen on mobile.
*/
interface BeforeInstallPromptEvent extends Event {
/**
* Returns an array of DOMString items containing the platforms on which the event was dispatched.
* This is provided for user agents that want to present a choice of versions to the user such as,
* for example, "web" or "play" which would allow the user to chose between a web version or
* an Android version.
*/
readonly platforms: Array<string>;
/**
* Returns a Promise that resolves to a DOMString containing either "accepted" or "dismissed".
*/
readonly userChoice: Promise<{
outcome: 'accepted' | 'dismissed',
platform: string
}>;
/**
* Allows a developer to show the install prompt at a time of their own choosing.
* This method returns a Promise.
*/
prompt(): Promise<void>;
}
interface WindowEventMap {
"beforeinstallprompt": BeforeInstallPromptEvent;
}

View File

@@ -33,6 +33,7 @@
height: 100%;
box-sizing: border-box;
overscroll-behavior: contain;
position: relative;
}
.logo-container {
@@ -170,6 +171,31 @@
--color: #fff;
}
.install-button {
composes: unbutton from '../../lib/util.scss';
&:hover,
&:focus {
background: #504488;
}
background: #5D509E;
border: 1px solid #e8e8e8;
color: #fff;
padding: 14px;
font-size: 1.3rem;
position: absolute;
top: 1rem;
right: 1rem;
animation: fade-in .3s ease-in-out;
}
@keyframes fade-in {
from { opacity: 0; }
}
.related-links {
display: flex;
padding: 0;

View File

@@ -12,8 +12,8 @@ export default class Select extends Component<Props, State> {
return (
<div class={style.select}>
<select class={`${style.nativeSelect} ${large ? style.large : ''}`} {...otherProps}/>
<svg class={style.arrow} viewBox="0 0 10 5"><path d="M0 0l5 5 5-5z"/></svg>
<select class={`${style.builtinSelect} ${large ? style.large : ''}`} {...otherProps} />
<svg class={style.arrow} viewBox="0 0 10 5"><path d="M0 0l5 5 5-5z" /></svg>
</div>
);
}

View File

@@ -2,7 +2,7 @@
position: relative;
}
.native-select {
.builtin-select {
background: #2f2f2f;
border-radius: 4px;
font: inherit;