Compare commits
1 Commits
dev
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
765901d8da |
@ -1,9 +1,9 @@
|
||||
# libavif and libaom versions are from
|
||||
# google3/third_party/libavif/METADATA
|
||||
CODEC_URL = https://github.com/AOMediaCodec/libavif/archive/647c3c208cf152395d777c1bf7240d2ecf7df5a9.tar.gz
|
||||
# https://docs.google.com/document/d/1wEEA5rRU7wT54k41u3qyZIZHDCJArIMzLuzsrLAwaK8/edit
|
||||
CODEC_URL = https://github.com/AOMediaCodec/libavif/archive/1c39e772c2c0d687691dd4b589a12c323f5f767d.tar.gz
|
||||
CODEC_PACKAGE = node_modules/libavif.tar.gz
|
||||
|
||||
LIBAOM_URL = https://aomedia.googlesource.com/aom/+archive/v3.6.0.tar.gz
|
||||
LIBAOM_URL = https://aomedia.googlesource.com/aom/+archive/v3.1.0.tar.gz
|
||||
LIBAOM_PACKAGE = node_modules/libaom.tar.gz
|
||||
|
||||
export CODEC_DIR = node_modules/libavif
|
||||
|
2
codecs/avif/dec/avif_dec.js
generated
2
codecs/avif/dec/avif_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/avif/dec/avif_node_dec.js
generated
2
codecs/avif/dec/avif_node_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/avif/enc/avif_enc.js
generated
2
codecs/avif/enc/avif_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/avif/enc/avif_enc_mt.js
generated
2
codecs/avif/enc/avif_enc_mt.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/avif/enc/avif_node_enc.js
generated
2
codecs/avif/enc/avif_node_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/avif/enc/avif_node_enc_mt.js
generated
2
codecs/avif/enc/avif_node_enc_mt.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
6
codecs/png/Cargo.lock
generated
6
codecs/png/Cargo.lock
generated
@ -1,5 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.1.0"
|
||||
@ -14,9 +16,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.4.0"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
|
@ -1,42 +0,0 @@
|
||||
CODEC_URL = https://github.com/phoboslab/qoi/archive/8d35d93cdca85d2868246c2a8a80a1e2c16ba2a8.tar.gz
|
||||
|
||||
CODEC_DIR = node_modules/qoi
|
||||
CODEC_BUILD_DIR:= $(CODEC_DIR)/build
|
||||
ENVIRONMENT = worker
|
||||
|
||||
OUT_JS = enc/qoi_enc.js dec/qoi_dec.js
|
||||
OUT_WASM := $(OUT_JS:.js=.wasm)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT_JS)
|
||||
|
||||
$(filter enc/%,$(OUT_JS)): enc/qoi_enc.o
|
||||
$(filter dec/%,$(OUT_JS)): dec/qoi_dec.o
|
||||
|
||||
# ALL .js FILES
|
||||
$(OUT_JS):
|
||||
$(LD) \
|
||||
$(LDFLAGS) \
|
||||
--bind \
|
||||
-s ENVIRONMENT=$(ENVIRONMENT) \
|
||||
-s EXPORT_ES6=1 \
|
||||
-o $@ \
|
||||
$+
|
||||
|
||||
# ALL .o FILES
|
||||
%.o: %.cpp $(CODEC_DIR)
|
||||
$(CXX) -c \
|
||||
$(CXXFLAGS) \
|
||||
-I $(CODEC_DIR) \
|
||||
-o $@ \
|
||||
$<
|
||||
|
||||
# CREATE DIRECTORY
|
||||
$(CODEC_DIR):
|
||||
mkdir -p $(CODEC_DIR)
|
||||
curl -sL $(CODEC_URL) | tar xz --strip 1 -C $(CODEC_DIR)
|
||||
|
||||
clean:
|
||||
$(RM) $(OUT_JS) $(OUT_WASM)
|
||||
$(MAKE) -C $(CODEC_DIR) clean
|
@ -1,30 +0,0 @@
|
||||
#include <emscripten/bind.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
#define QOI_IMPLEMENTATION
|
||||
#include "qoi.h"
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray");
|
||||
thread_local const val ImageData = val::global("ImageData");
|
||||
|
||||
val decode(std::string qoiimage) {
|
||||
qoi_desc desc;
|
||||
uint8_t* rgba = (uint8_t*)qoi_decode(qoiimage.c_str(), qoiimage.length(), &desc, 4);
|
||||
|
||||
// Resultant width and height stored in descriptor
|
||||
int decodedWidth = desc.width;
|
||||
int decodedHeight = desc.height;
|
||||
|
||||
val result = ImageData.new_(
|
||||
Uint8ClampedArray.new_(typed_memory_view(4 * decodedWidth * decodedHeight, rgba)),
|
||||
decodedWidth, decodedHeight);
|
||||
free(rgba);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_BINDINGS(my_module) {
|
||||
function("decode", &decode);
|
||||
}
|
7
codecs/qoi/dec/qoi_dec.d.ts
vendored
7
codecs/qoi/dec/qoi_dec.d.ts
vendored
@ -1,7 +0,0 @@
|
||||
export interface QOIModule extends EmscriptenWasm.Module {
|
||||
decode(data: BufferSource): ImageData | null;
|
||||
}
|
||||
|
||||
declare var moduleFactory: EmscriptenWasm.ModuleFactory<QOIModule>;
|
||||
|
||||
export default moduleFactory;
|
16
codecs/qoi/dec/qoi_dec.js
generated
16
codecs/qoi/dec/qoi_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,36 +0,0 @@
|
||||
#include <emscripten/bind.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
#define QOI_IMPLEMENTATION
|
||||
#include "qoi.h"
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
struct QoiOptions {};
|
||||
|
||||
thread_local const val Uint8Array = val::global("Uint8Array");
|
||||
|
||||
val encode(std::string buffer, int width, int height, QoiOptions options) {
|
||||
int compressedSizeInBytes;
|
||||
qoi_desc desc;
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.channels = 4;
|
||||
desc.colorspace = QOI_SRGB;
|
||||
|
||||
uint8_t* encodedData = (uint8_t*)qoi_encode(buffer.c_str(), &desc, &compressedSizeInBytes);
|
||||
if (encodedData == NULL)
|
||||
return val::null();
|
||||
|
||||
auto js_result =
|
||||
Uint8Array.new_(typed_memory_view(compressedSizeInBytes, (const uint8_t*)encodedData));
|
||||
free(encodedData);
|
||||
|
||||
return js_result;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_BINDINGS(my_module) {
|
||||
value_object<QoiOptions>("QoiOptions");
|
||||
|
||||
function("encode", &encode);
|
||||
}
|
14
codecs/qoi/enc/qoi_enc.d.ts
vendored
14
codecs/qoi/enc/qoi_enc.d.ts
vendored
@ -1,14 +0,0 @@
|
||||
export interface EncodeOptions {}
|
||||
|
||||
export interface QoiModule extends EmscriptenWasm.Module {
|
||||
encode(
|
||||
data: BufferSource,
|
||||
width: number,
|
||||
height: number,
|
||||
options: EncodeOptions,
|
||||
): Uint8Array;
|
||||
}
|
||||
|
||||
declare var moduleFactory: EmscriptenWasm.ModuleFactory<QoiModule>;
|
||||
|
||||
export default moduleFactory;
|
16
codecs/qoi/enc/qoi_enc.js
generated
16
codecs/qoi/enc/qoi_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "qoi",
|
||||
"scripts": {
|
||||
"build": "../build-cpp.sh"
|
||||
}
|
||||
}
|
||||
|
10
package-lock.json
generated
10
package-lock.json
generated
@ -8,6 +8,9 @@
|
||||
"name": "squoosh",
|
||||
"version": "2.0.0",
|
||||
"license": "apache-2.0",
|
||||
"dependencies": {
|
||||
"wasm-feature-detect": "^1.2.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.1.0",
|
||||
@ -43,7 +46,6 @@
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"serve": "^11.3.2",
|
||||
"typescript": "^4.4.4",
|
||||
"wasm-feature-detect": "^1.2.11",
|
||||
"which": "^2.0.2"
|
||||
}
|
||||
},
|
||||
@ -8559,8 +8561,7 @@
|
||||
"node_modules/wasm-feature-detect": {
|
||||
"version": "1.2.11",
|
||||
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
|
||||
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
@ -15756,8 +15757,7 @@
|
||||
"wasm-feature-detect": {
|
||||
"version": "1.2.11",
|
||||
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
|
||||
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
|
@ -2,26 +2,6 @@
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
--size: 17px;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background-color: var(--main-theme-color);
|
||||
border-radius: 999px;
|
||||
opacity: 0.25;
|
||||
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
transition-property: transform;
|
||||
transition-duration: 250ms;
|
||||
}
|
||||
|
||||
&:focus-within::before {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.real-checkbox {
|
||||
|
@ -47,10 +47,6 @@ range-input::before {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
range-input:focus-within .thumb {
|
||||
outline: white solid 2px;
|
||||
}
|
||||
|
||||
.thumb-wrapper {
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
|
@ -11,10 +11,6 @@
|
||||
padding: 3px calc(var(--thumb-size) / 2 + 3px);
|
||||
}
|
||||
|
||||
.checkbox:focus-within .track {
|
||||
outline: white solid 2px;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
position: relative;
|
||||
width: var(--thumb-size);
|
||||
|
@ -17,7 +17,7 @@ import Toggle from './Toggle';
|
||||
import Select from './Select';
|
||||
import { Options as QuantOptionsComponent } from 'features/processors/quantize/client';
|
||||
import { Options as ResizeOptionsComponent } from 'features/processors/resize/client';
|
||||
import { ImportIcon, SaveIcon, SwapIcon } from 'client/lazy-app/icons';
|
||||
import { SwapIcon } from 'client/lazy-app/icons';
|
||||
|
||||
interface Props {
|
||||
index: 0 | 1;
|
||||
@ -29,14 +29,10 @@ interface Props {
|
||||
onEncoderOptionsChange(index: 0 | 1, newOptions: EncoderOptions): void;
|
||||
onProcessorOptionsChange(index: 0 | 1, newOptions: ProcessorState): void;
|
||||
onCopyToOtherSideClick(index: 0 | 1): void;
|
||||
onSaveSideSettingsClick(index: 0 | 1): void;
|
||||
onImportSideSettingsClick(index: 0 | 1): void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
supportedEncoderMap?: PartialButNotUndefined<typeof encoderMap>;
|
||||
leftSideSettings?: string | null;
|
||||
rightSideSettings?: string | null;
|
||||
}
|
||||
|
||||
type PartialButNotUndefined<T> = {
|
||||
@ -64,8 +60,6 @@ const supportedEncoderMapP: Promise<PartialButNotUndefined<typeof encoderMap>> =
|
||||
export default class Options extends Component<Props, State> {
|
||||
state: State = {
|
||||
supportedEncoderMap: undefined,
|
||||
leftSideSettings: localStorage.getItem('leftSideSettings'),
|
||||
rightSideSettings: localStorage.getItem('rightSideSettings'),
|
||||
};
|
||||
|
||||
constructor() {
|
||||
@ -75,29 +69,6 @@ export default class Options extends Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
private setLeftSideSettings = () => {
|
||||
this.setState({
|
||||
leftSideSettings: localStorage.getItem('leftSideSettings'),
|
||||
});
|
||||
};
|
||||
|
||||
private setRightSideSettings = () => {
|
||||
this.setState({
|
||||
rightSideSettings: localStorage.getItem('rightSideSettings'),
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
// Changing the state when side setting is stored in localstorage
|
||||
window.addEventListener('leftSideSettings', this.setLeftSideSettings);
|
||||
window.addEventListener('rightSideSettings', this.setRightSideSettings);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
window.removeEventListener('leftSideSettings', this.setLeftSideSettings);
|
||||
window.removeEventListener('removeSideSettings', this.setRightSideSettings);
|
||||
}
|
||||
|
||||
private onEncoderTypeChange = (event: Event) => {
|
||||
const el = event.currentTarget as HTMLSelectElement;
|
||||
|
||||
@ -139,14 +110,6 @@ export default class Options extends Component<Props, State> {
|
||||
this.props.onCopyToOtherSideClick(this.props.index);
|
||||
};
|
||||
|
||||
private onSaveSideSettingClick = () => {
|
||||
this.props.onSaveSideSettingsClick(this.props.index);
|
||||
};
|
||||
|
||||
private onImportSideSettingsClick = () => {
|
||||
this.props.onImportSideSettingsClick(this.props.index);
|
||||
};
|
||||
|
||||
render(
|
||||
{ source, encoderState, processorState }: Props,
|
||||
{ supportedEncoderMap }: State,
|
||||
@ -176,36 +139,6 @@ export default class Options extends Component<Props, State> {
|
||||
>
|
||||
<SwapIcon />
|
||||
</button>
|
||||
<button
|
||||
class={style.saveButton}
|
||||
title="Save side settings"
|
||||
onClick={this.onSaveSideSettingClick}
|
||||
>
|
||||
<SaveIcon />
|
||||
</button>
|
||||
<button
|
||||
class={
|
||||
style.importButton +
|
||||
' ' +
|
||||
(!this.state.leftSideSettings && this.props.index === 0
|
||||
? style.buttonOpacity
|
||||
: '') +
|
||||
' ' +
|
||||
(!this.state.rightSideSettings && this.props.index === 1
|
||||
? style.buttonOpacity
|
||||
: '')
|
||||
}
|
||||
title="Import saved side settings"
|
||||
onClick={this.onImportSideSettingsClick}
|
||||
disabled={
|
||||
// Disabled if this side's settings haven't been saved
|
||||
(!this.state.leftSideSettings &&
|
||||
this.props.index === 0) ||
|
||||
(!this.state.rightSideSettings && this.props.index === 1)
|
||||
}
|
||||
>
|
||||
<ImportIcon />
|
||||
</button>
|
||||
</div>
|
||||
</h3>
|
||||
<label class={style.sectionEnabler}>
|
||||
@ -257,9 +190,7 @@ export default class Options extends Component<Props, State> {
|
||||
onChange={this.onEncoderTypeChange}
|
||||
large
|
||||
>
|
||||
<option value="identity">{`Original Image ${
|
||||
this.props.source ? `(${this.props.source.file.name})` : ''
|
||||
}`}</option>
|
||||
<option value="identity">Original Image</option>
|
||||
{Object.entries(supportedEncoderMap).map(([type, encoder]) => (
|
||||
<option value={type}>{encoder.meta.label}</option>
|
||||
))}
|
||||
|
@ -53,16 +53,6 @@
|
||||
composes: option-toggle;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 1em;
|
||||
|
||||
border-top: 1px solid #fff4;
|
||||
|
||||
transition-property: background-color;
|
||||
transition-duration: 250ms;
|
||||
}
|
||||
|
||||
.option-reveal:focus-within,
|
||||
.option-reveal:hover {
|
||||
background-color: #fff2;
|
||||
}
|
||||
|
||||
.option-one-cell {
|
||||
@ -83,11 +73,11 @@
|
||||
}
|
||||
|
||||
.text-field {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
background: var(--white);
|
||||
color: var(--black);
|
||||
font: inherit;
|
||||
border: none;
|
||||
padding: 6px 6px 6px 10px;
|
||||
padding: 6px 0 6px 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
@ -122,37 +112,7 @@
|
||||
.copy-over-button {
|
||||
composes: title-button;
|
||||
|
||||
/* Make the filled arrow point towards the other options element */
|
||||
transform: rotate(var(--rotate-copyoverbutton-angle));
|
||||
|
||||
svg {
|
||||
fill: var(--header-text-color);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: var(--header-text-color) solid 2px;
|
||||
outline-offset: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.save-button,
|
||||
.import-button {
|
||||
composes: title-button;
|
||||
|
||||
svg {
|
||||
stroke: var(--header-text-color);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: var(--header-text-color) solid 2px;
|
||||
outline-offset: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.button-opacity {
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
svg {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
@ -111,9 +111,6 @@ async function decodeImage(
|
||||
if (mimeType === 'image/webp2') {
|
||||
return await workerBridge.wp2Decode(signal, blob);
|
||||
}
|
||||
if (mimeType === 'image/qoi') {
|
||||
return await workerBridge.qoiDecode(signal, blob);
|
||||
}
|
||||
}
|
||||
// Otherwise fall through and try built-in decoding for a laugh.
|
||||
return await builtinDecode(signal, blob);
|
||||
@ -284,35 +281,24 @@ export default class Compress extends Component<Props, State> {
|
||||
source: undefined,
|
||||
loading: false,
|
||||
preprocessorState: defaultPreprocessorState,
|
||||
// Tasking catched side settings if available otherwise taking default settings
|
||||
sides: [
|
||||
localStorage.getItem('leftSideSettings')
|
||||
? {
|
||||
...JSON.parse(localStorage.getItem('leftSideSettings') as string),
|
||||
loading: false,
|
||||
}
|
||||
: {
|
||||
latestSettings: {
|
||||
processorState: defaultProcessorState,
|
||||
encoderState: undefined,
|
||||
},
|
||||
loading: false,
|
||||
},
|
||||
localStorage.getItem('rightSideSettings')
|
||||
? {
|
||||
...JSON.parse(localStorage.getItem('rightSideSettings') as string),
|
||||
loading: false,
|
||||
}
|
||||
: {
|
||||
latestSettings: {
|
||||
processorState: defaultProcessorState,
|
||||
encoderState: {
|
||||
type: 'mozJPEG',
|
||||
options: encoderMap.mozJPEG.meta.defaultOptions,
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
{
|
||||
latestSettings: {
|
||||
processorState: defaultProcessorState,
|
||||
encoderState: undefined,
|
||||
},
|
||||
loading: false,
|
||||
},
|
||||
{
|
||||
latestSettings: {
|
||||
processorState: defaultProcessorState,
|
||||
encoderState: {
|
||||
type: 'mozJPEG',
|
||||
options: encoderMap.mozJPEG.meta.defaultOptions,
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
},
|
||||
],
|
||||
mobileView: this.widthQuery.matches,
|
||||
};
|
||||
@ -442,99 +428,6 @@ export default class Compress extends Component<Props, State> {
|
||||
sides: cleanSet(this.state.sides, otherIndex, oldSettings),
|
||||
});
|
||||
};
|
||||
/**
|
||||
* This function saves encodedSettings and latestSettings of
|
||||
* particular side in browser local storage
|
||||
* @param index : (0|1)
|
||||
* @returns
|
||||
*/
|
||||
private onSaveSideSettingsClick = async (index: 0 | 1) => {
|
||||
if (index === 0) {
|
||||
const leftSideSettings = JSON.stringify({
|
||||
encodedSettings: this.state.sides[index].encodedSettings,
|
||||
latestSettings: this.state.sides[index].latestSettings,
|
||||
});
|
||||
localStorage.setItem('leftSideSettings', leftSideSettings);
|
||||
// Firing an event when we save side settings in localstorage
|
||||
window.dispatchEvent(new CustomEvent('leftSideSettings'));
|
||||
await this.props.showSnack('Left side settings saved', {
|
||||
timeout: 1500,
|
||||
actions: ['dismiss'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (index === 1) {
|
||||
const rightSideSettings = JSON.stringify({
|
||||
encodedSettings: this.state.sides[index].encodedSettings,
|
||||
latestSettings: this.state.sides[index].latestSettings,
|
||||
});
|
||||
localStorage.setItem('rightSideSettings', rightSideSettings);
|
||||
// Firing an event when we save side settings in localstorage
|
||||
window.dispatchEvent(new CustomEvent('rightSideSettings'));
|
||||
await this.props.showSnack('Right side settings saved', {
|
||||
timeout: 1500,
|
||||
actions: ['dismiss'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function sets the side state with catched localstorage
|
||||
* value as per side index provided
|
||||
* @param index : (0|1)
|
||||
* @returns
|
||||
*/
|
||||
private onImportSideSettingsClick = async (index: 0 | 1) => {
|
||||
const leftSideSettingsString = localStorage.getItem('leftSideSettings');
|
||||
const rightSideSettingsString = localStorage.getItem('rightSideSettings');
|
||||
|
||||
if (index === 0 && leftSideSettingsString) {
|
||||
const oldLeftSideSettings = this.state.sides[index];
|
||||
const newLeftSideSettings = {
|
||||
...this.state.sides[index],
|
||||
...JSON.parse(leftSideSettingsString),
|
||||
};
|
||||
this.setState({
|
||||
sides: cleanSet(this.state.sides, index, newLeftSideSettings),
|
||||
});
|
||||
const result = await this.props.showSnack('Left side settings imported', {
|
||||
timeout: 3000,
|
||||
actions: ['undo', 'dismiss'],
|
||||
});
|
||||
if (result === 'undo') {
|
||||
this.setState({
|
||||
sides: cleanSet(this.state.sides, index, oldLeftSideSettings),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (index === 1 && rightSideSettingsString) {
|
||||
const oldRightSideSettings = this.state.sides[index];
|
||||
const newRightSideSettings = {
|
||||
...this.state.sides[index],
|
||||
...JSON.parse(rightSideSettingsString),
|
||||
};
|
||||
this.setState({
|
||||
sides: cleanSet(this.state.sides, index, newRightSideSettings),
|
||||
});
|
||||
const result = await this.props.showSnack(
|
||||
'Right side settings imported',
|
||||
{
|
||||
timeout: 3000,
|
||||
actions: ['undo', 'dismiss'],
|
||||
},
|
||||
);
|
||||
if (result === 'undo') {
|
||||
this.setState({
|
||||
sides: cleanSet(this.state.sides, index, oldRightSideSettings),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
private onPreprocessorChange = async (
|
||||
preprocessorState: PreprocessorState,
|
||||
@ -936,8 +829,6 @@ export default class Compress extends Component<Props, State> {
|
||||
onEncoderOptionsChange={this.onEncoderOptionsChange}
|
||||
onProcessorOptionsChange={this.onProcessorOptionsChange}
|
||||
onCopyToOtherSideClick={this.onCopyToOtherClick}
|
||||
onSaveSideSettingsClick={this.onSaveSideSettingsClick}
|
||||
onImportSideSettingsClick={this.onImportSideSettingsClick}
|
||||
/>
|
||||
));
|
||||
|
||||
@ -951,7 +842,7 @@ export default class Compress extends Component<Props, State> {
|
||||
typeLabel={
|
||||
side.latestSettings.encoderState
|
||||
? encoderMap[side.latestSettings.encoderState.type].meta.label
|
||||
: `${side.file ? `${side.file.name}` : 'Original Image'}`
|
||||
: 'Original Image'
|
||||
}
|
||||
/>
|
||||
));
|
||||
|
@ -45,11 +45,9 @@
|
||||
--hot-theme-color: var(--hot-pink);
|
||||
--header-text-color: var(--white);
|
||||
--scroller-radius: var(--options-radius) var(--options-radius) 0 0;
|
||||
--rotate-copyoverbutton-angle: 90deg; /* To point down */
|
||||
|
||||
@media (min-width: 600px) {
|
||||
--scroller-radius: 0 var(--options-radius) var(--options-radius) 0;
|
||||
--rotate-copyoverbutton-angle: 0deg; /* To point right (no change) */
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,11 +56,9 @@
|
||||
--hot-theme-color: var(--deep-blue);
|
||||
--header-text-color: var(--dark-text);
|
||||
--scroller-radius: var(--options-radius) var(--options-radius) 0 0;
|
||||
--rotate-copyoverbutton-angle: -90deg; /* To point up */
|
||||
|
||||
@media (min-width: 600px) {
|
||||
--scroller-radius: var(--options-radius) 0 0 var(--options-radius);
|
||||
--rotate-copyoverbutton-angle: 180deg; /* To point left */
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,13 +109,6 @@
|
||||
|
||||
& > svg {
|
||||
width: 47px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&:focus .back-blob {
|
||||
stroke: var(--deep-blue);
|
||||
stroke-width: 5px;
|
||||
animation: strokePulse 500ms ease forwards;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
@ -131,15 +120,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes strokePulse {
|
||||
from {
|
||||
stroke-width: 8px;
|
||||
}
|
||||
to {
|
||||
stroke-width: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.back-blob {
|
||||
fill: var(--hot-pink);
|
||||
opacity: 0.77;
|
||||
|
@ -97,31 +97,3 @@ export const SwapIcon = () => (
|
||||
<path d="M5.5 3.6v6.8L2.1 7l3.4-3.4M7 0L0 7l7 7V0zm4 0v14l7-7-7-7z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const SaveIcon = () => (
|
||||
<svg viewBox="0 0 24 24">
|
||||
<g
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M12.501 20.93c-.866.25-1.914-.166-2.176-1.247a1.724 1.724 0 0 0-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 0 0-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 0 0 1.066-2.573c-.94-1.543.826-3.31 2.37-2.37c1 .608 2.296.07 2.572-1.065c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.074.26 1.49 1.296 1.252 2.158M19 22v-6m3 3l-3-3l-3 3" />
|
||||
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0-6 0" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const ImportIcon = () => (
|
||||
<svg viewBox="0 0 24 24">
|
||||
<g
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M12.52 20.924c-.87.262-1.93-.152-2.195-1.241a1.724 1.724 0 0 0-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 0 0-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 0 0 1.066-2.573c-.94-1.543.826-3.31 2.37-2.37c1 .608 2.296.07 2.572-1.065c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.088.264 1.502 1.323 1.242 2.192M19 16v6m3-3l-3 3l-3-3" />
|
||||
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0-6 0" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
@ -103,7 +103,6 @@ const magicNumberMapInput = [
|
||||
[/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/, 'image/avif'],
|
||||
[/^\xff\x0a/, 'image/jxl'],
|
||||
[/^\x00\x00\x00\x0cJXL \x0d\x0a\x87\x0a/, 'image/jxl'],
|
||||
[/^qoif/, 'image/qoi'],
|
||||
] as const;
|
||||
|
||||
export type ImageMimeTypes = typeof magicNumberMapInput[number][1];
|
||||
|
@ -1,19 +0,0 @@
|
||||
import qoiDecoder, { QOIModule } from 'codecs/qoi/dec/qoi_dec';
|
||||
import { initEmscriptenModule, blobToArrayBuffer } from 'features/worker-utils';
|
||||
|
||||
let emscriptenModule: Promise<QOIModule>;
|
||||
|
||||
export default async function decode(blob: Blob): Promise<ImageData> {
|
||||
if (!emscriptenModule) {
|
||||
emscriptenModule = initEmscriptenModule(qoiDecoder);
|
||||
}
|
||||
|
||||
const [module, data] = await Promise.all([
|
||||
emscriptenModule,
|
||||
blobToArrayBuffer(blob),
|
||||
]);
|
||||
|
||||
const result = module.decode(data);
|
||||
if (!result) throw new Error('Decoding error');
|
||||
return result;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { EncodeOptions } from '../shared/meta';
|
||||
import type WorkerBridge from 'client/lazy-app/worker-bridge';
|
||||
|
||||
export function encode(
|
||||
signal: AbortSignal,
|
||||
workerBridge: WorkerBridge,
|
||||
imageData: ImageData,
|
||||
options: EncodeOptions,
|
||||
) {
|
||||
return workerBridge.qoiEncode(signal, imageData, options);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* 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" />
|
@ -1,19 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
import { EncodeOptions } from 'codecs/qoi/enc/qoi_enc';
|
||||
export { EncodeOptions };
|
||||
|
||||
export const label = 'QOI';
|
||||
export const mimeType = 'image/qoi';
|
||||
export const extension = 'qoi';
|
||||
export const defaultOptions: EncodeOptions = {};
|
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* 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" />
|
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* 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" />
|
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
import qoiEncoder, { QoiModule } from 'codecs/qoi/enc/qoi_enc';
|
||||
import type { EncodeOptions } from '../shared/meta';
|
||||
import { initEmscriptenModule } from 'features/worker-utils';
|
||||
|
||||
let emscriptenModule: Promise<QoiModule>;
|
||||
|
||||
async function init() {
|
||||
return initEmscriptenModule(qoiEncoder);
|
||||
}
|
||||
|
||||
export default async function encode(
|
||||
data: ImageData,
|
||||
options: EncodeOptions,
|
||||
): Promise<ArrayBuffer> {
|
||||
if (!emscriptenModule) {
|
||||
emscriptenModule = init();
|
||||
}
|
||||
|
||||
const module = await emscriptenModule;
|
||||
const resultView = module.encode(data.data, data.width, data.height, options);
|
||||
// wasm can’t run on SharedArrayBuffers, so we hard-cast to ArrayBuffer.
|
||||
return resultView.buffer as ArrayBuffer;
|
||||
}
|
@ -62,7 +62,7 @@ snack-bar {
|
||||
position: relative;
|
||||
flex: 0 1 auto;
|
||||
padding: 8px;
|
||||
height: 100%;
|
||||
height: 36px;
|
||||
margin: auto 8px auto -8px;
|
||||
min-width: 5em;
|
||||
background: none;
|
||||
@ -78,7 +78,6 @@ snack-bar {
|
||||
overflow: hidden;
|
||||
transition: background-color 200ms ease;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
|
@ -24,28 +24,28 @@ import SlideOnScroll from './SlideOnScroll';
|
||||
const demos = [
|
||||
{
|
||||
description: 'Large photo',
|
||||
size: '2.8MB',
|
||||
size: '2.8mb',
|
||||
filename: 'photo.jpg',
|
||||
url: largePhoto,
|
||||
iconUrl: largePhotoIcon,
|
||||
},
|
||||
{
|
||||
description: 'Artwork',
|
||||
size: '2.9MB',
|
||||
size: '2.9mb',
|
||||
filename: 'art.jpg',
|
||||
url: artwork,
|
||||
iconUrl: artworkIcon,
|
||||
},
|
||||
{
|
||||
description: 'Device screen',
|
||||
size: '1.6MB',
|
||||
size: '1.6mb',
|
||||
filename: 'pixel3.png',
|
||||
url: deviceScreen,
|
||||
iconUrl: deviceScreenIcon,
|
||||
},
|
||||
{
|
||||
description: 'SVG icon',
|
||||
size: '13KB',
|
||||
size: '13k',
|
||||
filename: 'squoosh.svg',
|
||||
url: logo,
|
||||
iconUrl: logoIcon,
|
||||
@ -319,7 +319,7 @@ export default class Intro extends Component<Props, State> {
|
||||
class="unbutton"
|
||||
onClick={(event) => this.onDemoClick(i, event)}
|
||||
>
|
||||
<div class={style.demoContainer}>
|
||||
<div>
|
||||
<div class={style.demoIconContainer}>
|
||||
<img
|
||||
class={style.demoIcon}
|
||||
|
@ -321,13 +321,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
transition: scale 400ms ease-in-out;
|
||||
&:hover {
|
||||
scale: 1.05;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-size {
|
||||
background: var(--dim-blue);
|
||||
border-radius: 1000px;
|
||||
|
@ -18,14 +18,3 @@ html {
|
||||
/* Old stuff: */
|
||||
--button-fg: rgb(95, 180, 228);
|
||||
}
|
||||
|
||||
@media (dynamic-range: high) {
|
||||
@supports (color: oklch(0% 0 0)) {
|
||||
html {
|
||||
--pink: oklch(75% 0.3 3);
|
||||
--hot-pink: oklch(65% 0.3 3);
|
||||
--blue: oklch(75% 0.3 248);
|
||||
--deep-blue: oklch(65% 0.3 248);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,6 @@ import favicon from 'url:static-build/assets/favicon.ico';
|
||||
import ogImage from 'url:static-build/assets/icon-large-maskable.png';
|
||||
import { escapeStyleScriptContent, siteOrigin } from 'static-build/utils';
|
||||
import Intro from 'shared/prerendered-app/Intro';
|
||||
import snackbarCss from 'css:../../../shared/custom-els/snack-bar/styles.css';
|
||||
import * as snackbarStyle from '../../../shared/custom-els/snack-bar/styles.css';
|
||||
|
||||
interface Props {}
|
||||
|
||||
@ -75,29 +73,6 @@ const Index: FunctionalComponent<Props> = () => (
|
||||
<body>
|
||||
<div id="app">
|
||||
<Intro />
|
||||
<noscript>
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: escapeStyleScriptContent(snackbarCss),
|
||||
}}
|
||||
/>
|
||||
<snack-bar>
|
||||
<div
|
||||
class={snackbarStyle.snackbar}
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
aria-hidden="false"
|
||||
>
|
||||
<div class={snackbarStyle.text}>
|
||||
Initialization error: This site requires JavaScript, which is
|
||||
disabled in your browser.
|
||||
</div>
|
||||
<a class={snackbarStyle.button} href="/">
|
||||
reload
|
||||
</a>
|
||||
</div>
|
||||
</snack-bar>
|
||||
</noscript>
|
||||
</div>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
|
Reference in New Issue
Block a user