Compare commits
1 Commits
preprocess
...
jxl-rebuil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67ec215622 |
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh -e
|
||||
docker build -t squoosh-cpp - < ../cpp.Dockerfile
|
||||
docker run -it --rm -v $PWD:/src squoosh-cpp "$@"
|
||||
docker run -it --rm -v $PWD:/src squoosh-cpp
|
||||
|
||||
@@ -1,37 +1,18 @@
|
||||
CODEC_URL = https://gitlab.com/wg1/jpeg-xl.git
|
||||
CODEC_VERSION = v0.1
|
||||
CODEC_DIR = node_modules/jxl
|
||||
CODEC_BUILD_ROOT := $(CODEC_DIR)/build
|
||||
CODEC_MT_BUILD_DIR := $(CODEC_BUILD_ROOT)/mt
|
||||
CODEC_MT_SIMD_BUILD_DIR := $(CODEC_BUILD_ROOT)/mt-simd
|
||||
CODEC_BUILD_DIR := $(CODEC_DIR)/build
|
||||
CODEC_OUT := $(CODEC_BUILD_DIR)/lib/libjxl.a
|
||||
|
||||
OUT_JS = enc/jxl_enc.js enc/jxl_enc_mt.js enc/jxl_enc_mt_simd.js dec/jxl_dec.js
|
||||
OUT_JS = enc/jxl_enc.js dec/jxl_dec.js
|
||||
OUT_WASM = $(OUT_JS:.js=.wasm)
|
||||
OUT_WORKER = $(OUT_JS:.js=.worker.js)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT_JS)
|
||||
|
||||
# Define dependencies for all variations of build artifacts.
|
||||
$(filter enc/%,$(OUT_JS)): enc/jxl_enc.cpp
|
||||
$(filter dec/%,$(OUT_JS)): dec/jxl_dec.cpp
|
||||
|
||||
# For single-threaded build, we compile with threads enabled, but then just don't use them nor link them in.
|
||||
enc/jxl_enc.js enc/jxl_enc_mt.js dec/jxl_dec.js: CODEC_BUILD_DIR:=$(CODEC_MT_BUILD_DIR)
|
||||
enc/jxl_enc_mt_simd.js: CODEC_BUILD_DIR:=$(CODEC_MT_SIMD_BUILD_DIR)
|
||||
|
||||
enc/jxl_enc.js dec/jxl_dec.js: $(CODEC_MT_BUILD_DIR)/lib/libjxl.a
|
||||
enc/jxl_enc_mt.js: $(CODEC_MT_BUILD_DIR)/lib/libjxl.a $(CODEC_MT_BUILD_DIR)/lib/libjxl_threads.a
|
||||
enc/jxl_enc_mt_simd.js: $(CODEC_MT_SIMD_BUILD_DIR)/lib/libjxl.a $(CODEC_MT_SIMD_BUILD_DIR)/lib/libjxl_threads.a
|
||||
|
||||
# Compile multithreaded wrappers with -pthread.
|
||||
enc/jxl_enc_mt.js enc/jxl_enc_mt_simd.js: CXXFLAGS+=-pthread
|
||||
|
||||
$(OUT_JS):
|
||||
%.js: %.cpp $(LIBAOM_OUT) $(CODEC_OUT)
|
||||
$(CXX) \
|
||||
$(CXXFLAGS) \
|
||||
$(LDFLAGS) \
|
||||
-I $(CODEC_DIR) \
|
||||
-I $(CODEC_DIR)/lib \
|
||||
-I $(CODEC_DIR)/lib/include \
|
||||
@@ -40,6 +21,8 @@ $(OUT_JS):
|
||||
-I $(CODEC_DIR)/third_party/skcms \
|
||||
-I $(CODEC_DIR)/third_party/brunsli \
|
||||
-I $(CODEC_DIR)/third_party/brunsli/c/include \
|
||||
${CXXFLAGS} \
|
||||
${LDFLAGS} \
|
||||
--bind \
|
||||
--closure 1 \
|
||||
-s ALLOW_MEMORY_GROWTH=1 \
|
||||
@@ -59,31 +42,18 @@ $(OUT_JS):
|
||||
$(CODEC_BUILD_DIR)/third_party/libskcms.a \
|
||||
$(CODEC_BUILD_DIR)/third_party/highway/libhwy.a
|
||||
|
||||
%/lib/libjxl.a: %/Makefile
|
||||
$(MAKE) -C $(<D) jxl-static
|
||||
$(CODEC_OUT): $(CODEC_DIR)/CMakeLists.txt
|
||||
mkdir -p $(CODEC_BUILD_DIR)
|
||||
cd $(CODEC_BUILD_DIR) && \
|
||||
emcmake cmake ../ && \
|
||||
$(MAKE) jxl-static
|
||||
|
||||
%/lib/libjxl_threads.a: %/Makefile
|
||||
$(MAKE) -C $(<D) jxl_threads-static
|
||||
$(CODEC_DIR)/CMakeLists.txt: $(CODEC_DIR)
|
||||
|
||||
# Enable SIMD on a SIMD build.
|
||||
$(CODEC_MT_SIMD_BUILD_DIR)/Makefile: CXXFLAGS+=-msimd128
|
||||
|
||||
%/Makefile: $(CODEC_DIR)/CMakeLists.txt
|
||||
emcmake cmake \
|
||||
$(CMAKE_FLAGS) \
|
||||
-DBUILD_SHARED_LIBS=0 \
|
||||
-DJPEGXL_ENABLE_BENCHMARK=0 \
|
||||
-DJPEGXL_ENABLE_EXAMPLES=0 \
|
||||
-DBUILD_TESTING=0 \
|
||||
-B $(@D) \
|
||||
$(<D)
|
||||
|
||||
$(CODEC_DIR)/CMakeLists.txt:
|
||||
mkdir -p $(@D)
|
||||
git clone $(CODEC_URL) --recursive -j`nproc` --depth 1 --branch $(CODEC_VERSION) $(@D)
|
||||
$(CODEC_DIR):
|
||||
mkdir -p $@
|
||||
git clone $(CODEC_URL) --recursive -j`nproc` --depth 1 --branch $(CODEC_VERSION) $@
|
||||
|
||||
clean:
|
||||
$(RM) $(OUT_JS) $(OUT_WASM) $(OUT_WORKER)
|
||||
$(RM) $(OUT_JS) $(OUT_WASM)
|
||||
$(MAKE) -C $(CODEC_BUILD_DIR) clean
|
||||
$(MAKE) -C $(CODEC_MT_BUILD_DIR) clean
|
||||
$(MAKE) -C $(CODEC_MT_SIMD_BUILD_DIR) clean
|
||||
|
||||
1241
codecs/jxl/dec/jxl_dec.js
generated
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "lib/jxl/enc_file.h"
|
||||
#include "lib/jxl/external_image.h"
|
||||
#include "lib/jxl/base/thread_pool_internal.h"
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
@@ -26,11 +25,6 @@ val encode(std::string image, int width, int height, JXLOptions options) {
|
||||
jxl::CodecInOut io;
|
||||
jxl::PaddedBytes bytes;
|
||||
jxl::ImageBundle* main = &io.Main();
|
||||
jxl::ThreadPoolInternal *pool_ptr = nullptr;
|
||||
#ifdef __EMSCRIPTEN_PTHREADS__
|
||||
jxl::ThreadPoolInternal pool;
|
||||
pool_ptr = &pool;
|
||||
#endif
|
||||
|
||||
cparams.epf = options.epf;
|
||||
cparams.speed_tier = static_cast<jxl::SpeedTier>(options.speed);
|
||||
@@ -103,14 +97,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_alpha=*/8, /*bits_per_sample=*/8,
|
||||
/*big_endian=*/false, /*flipped_y=*/false, pool_ptr, main);
|
||||
/*big_endian=*/false, /*flipped_y=*/false, /*pool=*/nullptr, main);
|
||||
|
||||
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)) {
|
||||
js_result = Uint8Array.new_(typed_memory_view(bytes.size(), bytes.data()));
|
||||
}
|
||||
|
||||
|
||||
1
codecs/jxl/enc/jxl_enc_mt.d.ts
vendored
@@ -1 +0,0 @@
|
||||
export { default } from './jxl_enc';
|
||||
111
codecs/jxl/enc/jxl_enc_mt.js
generated
@@ -1,111 +0,0 @@
|
||||
|
||||
var jxl_enc_mt = (function() {
|
||||
var _scriptDir = import.meta.url;
|
||||
|
||||
return (
|
||||
function(jxl_enc_mt) {
|
||||
jxl_enc_mt = jxl_enc_mt || {};
|
||||
|
||||
|
||||
function e(){m.buffer!=n&&u(m.buffer);return aa}function v(){m.buffer!=n&&u(m.buffer);return ba}function x(){m.buffer!=n&&u(m.buffer);return ca}function ea(){m.buffer!=n&&u(m.buffer);return fa}function A(){m.buffer!=n&&u(m.buffer);return ha}function C(){m.buffer!=n&&u(m.buffer);return ia}function ja(){m.buffer!=n&&u(m.buffer);return ka}function la(){m.buffer!=n&&u(m.buffer);return ma}var D;D||(D=typeof jxl_enc_mt !== 'undefined' ? jxl_enc_mt : {});var na,oa;
|
||||
D.ready=new Promise(function(a,b){na=a;oa=b});var E={},F;for(F in D)D.hasOwnProperty(F)&&(E[F]=D[F]);var pa="./this.program",G=D.ENVIRONMENT_IS_PTHREAD||!1;G&&(n=D.buffer);var H="";function qa(a){return D.locateFile?D.locateFile(a,H):H+a}var ra;H=self.location.href;_scriptDir&&(H=_scriptDir);0!==H.indexOf("blob:")?H=H.substr(0,H.lastIndexOf("/")+1):H="";ra=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)};
|
||||
var sa=D.print||console.log.bind(console),J=D.printErr||console.warn.bind(console);for(F in E)E.hasOwnProperty(F)&&(D[F]=E[F]);E=null;D.thisProgram&&(pa=D.thisProgram);var ta;D.wasmBinary&&(ta=D.wasmBinary);var noExitRuntime;D.noExitRuntime&&(noExitRuntime=D.noExitRuntime);"object"!==typeof WebAssembly&&K("no native wasm support detected");var m,ua,threadInfoStruct=0,selfThreadId=0,va=!1;function wa(a,b){a||K("Assertion failed: "+b)}
|
||||
function xa(a,b,c){c=b+c;for(var d="";!(b>=c);){var f=a[b++];if(!f)break;if(f&128){var g=a[b++]&63;if(192==(f&224))d+=String.fromCharCode((f&31)<<6|g);else{var l=a[b++]&63;f=224==(f&240)?(f&15)<<12|g<<6|l:(f&7)<<18|g<<12|l<<6|a[b++]&63;65536>f?d+=String.fromCharCode(f):(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023))}}else d+=String.fromCharCode(f)}return d}function L(a,b){return a?xa(v(),a,b):""}
|
||||
function ya(a,b,c,d){if(0<d){d=c+d-1;for(var f=0;f<a.length;++f){var g=a.charCodeAt(f);if(55296<=g&&57343>=g){var l=a.charCodeAt(++f);g=65536+((g&1023)<<10)|l&1023}if(127>=g){if(c>=d)break;b[c++]=g}else{if(2047>=g){if(c+1>=d)break;b[c++]=192|g>>6}else{if(65535>=g){if(c+2>=d)break;b[c++]=224|g>>12}else{if(c+3>=d)break;b[c++]=240|g>>18;b[c++]=128|g>>12&63}b[c++]=128|g>>6&63}b[c++]=128|g&63}}b[c]=0}}function za(a,b,c){ya(a,v(),b,c)}
|
||||
function Aa(a){for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);55296<=d&&57343>=d&&(d=65536+((d&1023)<<10)|a.charCodeAt(++c)&1023);127>=d?++b:b=2047>=d?b+2:65535>=d?b+3:b+4}return b}function Ba(a,b){for(var c=0,d="";;){var f=x()[a+2*c>>1];if(0==f||c==b/2)return d;++c;d+=String.fromCharCode(f)}}function Ca(a,b,c){void 0===c&&(c=2147483647);if(2>c)return 0;c-=2;var d=b;c=c<2*a.length?c/2:a.length;for(var f=0;f<c;++f){var g=a.charCodeAt(f);x()[b>>1]=g;b+=2}x()[b>>1]=0;return b-d}
|
||||
function Da(a){return 2*a.length}function Ea(a,b){for(var c=0,d="";!(c>=b/4);){var f=A()[a+4*c>>2];if(0==f)break;++c;65536<=f?(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023)):d+=String.fromCharCode(f)}return d}function Fa(a,b,c){void 0===c&&(c=2147483647);if(4>c)return 0;var d=b;c=d+c-4;for(var f=0;f<a.length;++f){var g=a.charCodeAt(f);if(55296<=g&&57343>=g){var l=a.charCodeAt(++f);g=65536+((g&1023)<<10)|l&1023}A()[b>>2]=g;b+=4;if(b+4>c)break}A()[b>>2]=0;return b-d}
|
||||
function Ga(a){for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);55296<=d&&57343>=d&&++c;b+=4}return b}function Ha(a,b){e().set(a,b)}var n,aa,ba,ca,fa,ha,ia,ka,ma;function u(a){n=a;D.HEAP8=aa=new Int8Array(a);D.HEAP16=ca=new Int16Array(a);D.HEAP32=ha=new Int32Array(a);D.HEAPU8=ba=new Uint8Array(a);D.HEAPU16=fa=new Uint16Array(a);D.HEAPU32=ia=new Uint32Array(a);D.HEAPF32=ka=new Float32Array(a);D.HEAPF64=ma=new Float64Array(a)}var Ia=D.INITIAL_MEMORY||16777216;
|
||||
if(G)m=D.wasmMemory,n=D.buffer;else if(D.wasmMemory)m=D.wasmMemory;else if(m=new WebAssembly.Memory({initial:Ia/65536,maximum:32768,shared:!0}),!(m.buffer instanceof SharedArrayBuffer))throw J("requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag"),Error("bad memory");m&&(n=m.buffer);Ia=n.byteLength;u(n);var M,Ja=[],Ka=[],La=[],Ma=[];
|
||||
function Na(){var a=D.preRun.shift();Ja.unshift(a)}var N=0,Oa=null,Pa=null;D.preloadedImages={};D.preloadedAudios={};function K(a){if(D.onAbort)D.onAbort(a);G&&console.error("Pthread aborting at "+Error().stack);J(a);va=!0;a=new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");oa(a);throw a;}function Qa(){var a=O;return String.prototype.startsWith?a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}var O="jxl_enc_mt.wasm";
|
||||
Qa()||(O=qa(O));function Ra(){try{if(ta)return new Uint8Array(ta);if(ra)return ra(O);throw"both async and sync fetching of the wasm failed";}catch(a){K(a)}}function Sa(){return ta||"function"!==typeof fetch?Promise.resolve().then(Ra):fetch(O,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+O+"'";return a.arrayBuffer()}).catch(function(){return Ra()})}var Ua={60005:function(a,b){setTimeout(function(){Ta(a,b)},0)},60083:function(){throw"Canceled!";}};
|
||||
function Va(a){for(;0<a.length;){var b=a.shift();if("function"==typeof b)b(D);else{var c=b.Bb;"number"===typeof c?void 0===b.Va?M.get(c)():M.get(c)(b.Va):c(void 0===b.Va?null:b.Va)}}}function Wa(a,b,c){var d;-1!=a.indexOf("j")?d=c&&c.length?D["dynCall_"+a].apply(null,[b].concat(c)):D["dynCall_"+a].call(null,b):d=M.get(b).apply(null,c);return d}D.dynCall=Wa;var P=0,Xa=0,Ya=0;function Za(a,b,c){P=a|0;Ya=b|0;Xa=c|0}D.registerPthreadPtr=Za;
|
||||
function $a(a,b){if(0>=a||a>e().length||a&1||0>b)return-28;if(0==b)return 0;2147483647<=b&&(b=Infinity);var c=Atomics.load(A(),Q.rb>>2),d=0;if(c==a&&Atomics.compareExchange(A(),Q.rb>>2,c,0)==c&&(--b,d=1,0>=b))return 1;a=Atomics.notify(A(),a>>2,b);if(0<=a)return a+d;throw"Atomics.notify returned an unexpected value "+a;}D._emscripten_futex_wake=$a;
|
||||
function ab(a){if(G)throw"Internal Error! cleanupThread() can only ever be called from main application thread!";if(!a)throw"Internal Error! Null pthread_ptr in cleanupThread!";A()[a+12>>2]=0;(a=Q.Oa[a])&&Q.bb(a.worker)}
|
||||
var Q={ic:1,pc:{ub:0,vb:0},Ma:[],Qa:[],Jb:function(){for(var a=navigator.hardwareConcurrency,b=0;b<a;++b)Q.lb()},Kb:function(){Q.Ka=R(232);for(var a=0;58>a;++a)C()[Q.Ka/4+a]=0;A()[Q.Ka+12>>2]=Q.Ka;a=Q.Ka+156;A()[a>>2]=a;var b=R(512);for(a=0;128>a;++a)C()[b/4+a]=0;Atomics.store(C(),Q.Ka+104>>2,b);Atomics.store(C(),Q.Ka+40>>2,Q.Ka);Atomics.store(C(),Q.Ka+44>>2,42);Q.pb();Za(Q.Ka,!1,1);bb(Q.Ka)},Lb:function(){Q.pb();na(D);Q.receiveObjectTransfer=Q.Qb;Q.setThreadStatus=Q.Rb;Q.threadCancel=Q.ac;Q.threadExit=
|
||||
Q.bc},pb:function(){Q.rb=cb},Oa:{},kb:[],Rb:function(){},tb:function(){for(;0<Q.kb.length;)Q.kb.pop()();G&&threadInfoStruct&&db()},bc:function(a){var b=P|0;b&&(Atomics.store(C(),b+4>>2,a),Atomics.store(C(),b+0>>2,1),Atomics.store(C(),b+60>>2,1),Atomics.store(C(),b+64>>2,0),Q.tb(),$a(b+0,2147483647),Za(0,0,0),threadInfoStruct=0,G&&postMessage({cmd:"exit"}))},ac:function(){Q.tb();Atomics.store(C(),threadInfoStruct+4>>2,-1);Atomics.store(C(),threadInfoStruct+0>>2,1);$a(threadInfoStruct+0,2147483647);
|
||||
threadInfoStruct=selfThreadId=0;Za(0,0,0);postMessage({cmd:"cancelDone"})},wc:function(){for(var a in Q.Oa){var b=Q.Oa[a];b&&b.worker&&Q.bb(b.worker)}Q.Oa={};for(a=0;a<Q.Ma.length;++a){var c=Q.Ma[a];c.terminate()}Q.Ma=[];for(a=0;a<Q.Qa.length;++a)c=Q.Qa[a],b=c.La,Q.ib(b),c.terminate();Q.Qa=[]},ib:function(a){if(a){if(a.threadInfoStruct){var b=A()[a.threadInfoStruct+104>>2];A()[a.threadInfoStruct+104>>2]=0;S(b);S(a.threadInfoStruct)}a.threadInfoStruct=0;a.gb&&a.Ra&&S(a.Ra);a.Ra=0;a.worker&&(a.worker.La=
|
||||
null)}},bb:function(a){delete Q.Oa[a.La.wb];Q.Ma.push(a);Q.Qa.splice(Q.Qa.indexOf(a),1);Q.ib(a.La);a.La=void 0},Qb:function(){},qb:function(a,b){a.onmessage=function(c){var d=c.data,f=d.cmd;a.La&&(Q.hb=a.La.threadInfoStruct);if(d.targetThread&&d.targetThread!=(P|0)){var g=Q.Oa[d.vc];g?g.worker.postMessage(c.data,d.transferList):console.error('Internal error! Worker sent a message "'+f+'" to target pthread '+d.targetThread+", but that thread no longer exists!")}else if("processQueuedMainThreadWork"===
|
||||
f)eb();else if("spawnThread"===f)fb(c.data);else if("cleanupThread"===f)ab(d.thread);else if("killThread"===f){c=d.thread;if(G)throw"Internal Error! killThread() can only ever be called from main application thread!";if(!c)throw"Internal Error! Null pthread_ptr in killThread!";A()[c+12>>2]=0;c=Q.Oa[c];c.worker.terminate();Q.ib(c);Q.Qa.splice(Q.Qa.indexOf(c.worker),1);c.worker.La=void 0}else if("cancelThread"===f){c=d.thread;if(G)throw"Internal Error! cancelThread() can only ever be called from main application thread!";
|
||||
if(!c)throw"Internal Error! Null pthread_ptr in cancelThread!";Q.Oa[c].worker.postMessage({cmd:"cancel"})}else"loaded"===f?(a.loaded=!0,b&&b(a),a.Xa&&(a.Xa(),delete a.Xa)):"print"===f?sa("Thread "+d.threadId+": "+d.text):"printErr"===f?J("Thread "+d.threadId+": "+d.text):"alert"===f?alert("Thread "+d.threadId+": "+d.text):"exit"===f?a.La&&Atomics.load(C(),a.La.wb+68>>2)&&Q.bb(a):"cancelDone"===f?Q.bb(a):"objectTransfer"!==f&&("setimmediate"===c.data.target?a.postMessage(c.data):J("worker sent an unknown command "+
|
||||
f));Q.hb=void 0};a.onerror=function(c){J("pthread sent an error! "+c.filename+":"+c.lineno+": "+c.message)};a.postMessage({cmd:"load",urlOrBlob:D.mainScriptUrlOrBlob||_scriptDir,wasmMemory:m,wasmModule:ua})},lb:function(){var a=qa("jxl_enc_mt.worker.js");Q.Ma.push(new Worker(a))},Cb:function(){0==Q.Ma.length&&(Q.lb(),Q.qb(Q.Ma[0]));return 0<Q.Ma.length?Q.Ma.pop():null},jc:function(a){for(a=performance.now()+a;performance.now()<a;);}};D.establishStackSpace=function(a){gb(a)};D.getNoExitRuntime=function(){return noExitRuntime};
|
||||
var hb;hb=G?function(){return performance.now()-D.__performance_now_clock_drift}:function(){return performance.now()};function ib(a,b){Q.kb.push(function(){M.get(a)(b)})}function jb(a){this.Wa=a-16;this.Wb=function(b){A()[this.Wa+8>>2]=b};this.Tb=function(b){A()[this.Wa+0>>2]=b};this.Ub=function(){A()[this.Wa+4>>2]=0};this.Sb=function(){var b=0;e()[this.Wa+12>>0]=b};this.Vb=function(){var b=0;e()[this.Wa+13>>0]=b};this.Hb=function(b,c){this.Wb(b);this.Tb(c);this.Ub();this.Sb();this.Vb()}}
|
||||
function kb(){return 0<kb.xb}var lb={};function mb(a){for(;a.length;){var b=a.pop();a.pop()(b)}}function nb(a){return this.fromWireType(C()[a>>2])}var T={},U={},ob={};function pb(a){if(void 0===a)return"_unknown";a=a.replace(/[^a-zA-Z0-9_]/g,"$");var b=a.charCodeAt(0);return 48<=b&&57>=b?"_"+a:a}function qb(a,b){a=pb(a);return(new Function("body","return function "+a+'() {\n "use strict"; return body.apply(this, arguments);\n};\n'))(b)}
|
||||
function rb(a){var b=Error,c=qb(a,function(d){this.name=a;this.message=d;d=Error(d).stack;void 0!==d&&(this.stack=this.toString()+"\n"+d.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(b.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message};return c}var sb=void 0;
|
||||
function tb(a,b,c){function d(k){k=c(k);if(k.length!==a.length)throw new sb("Mismatched type converter count");for(var q=0;q<a.length;++q)V(a[q],k[q])}a.forEach(function(k){ob[k]=b});var f=Array(b.length),g=[],l=0;b.forEach(function(k,q){U.hasOwnProperty(k)?f[q]=U[k]:(g.push(k),T.hasOwnProperty(k)||(T[k]=[]),T[k].push(function(){f[q]=U[k];++l;l===g.length&&d(f)}))});0===g.length&&d(f)}
|
||||
function ub(a){switch(a){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+a);}}var vb=void 0;function W(a){for(var b="";v()[a];)b+=vb[v()[a++]];return b}var wb=void 0;function X(a){throw new wb(a);}
|
||||
function V(a,b,c){c=c||{};if(!("argPackAdvance"in b))throw new TypeError("registerType registeredInstance requires argPackAdvance");var d=b.name;a||X('type "'+d+'" must have a positive integer typeid pointer');if(U.hasOwnProperty(a)){if(c.Gb)return;X("Cannot register type '"+d+"' twice")}U[a]=b;delete ob[a];T.hasOwnProperty(a)&&(b=T[a],delete T[a],b.forEach(function(f){f()}))}var xb=[],Y=[{},{value:void 0},{value:null},{value:!0},{value:!1}];
|
||||
function yb(a){4<a&&0===--Y[a].jb&&(Y[a]=void 0,xb.push(a))}function zb(a){switch(a){case void 0:return 1;case null:return 2;case !0:return 3;case !1:return 4;default:var b=xb.length?xb.pop():Y.length;Y[b]={jb:1,value:a};return b}}function Ab(a){if(null===a)return"null";var b=typeof a;return"object"===b||"array"===b||"function"===b?a.toString():""+a}
|
||||
function Bb(a,b){switch(b){case 2:return function(c){return this.fromWireType(ja()[c>>2])};case 3:return function(c){return this.fromWireType(la()[c>>3])};default:throw new TypeError("Unknown float type: "+a);}}function Cb(a){var b=Function;if(!(b instanceof Function))throw new TypeError("new_ called with constructor type "+typeof b+" which is not a function");var c=qb(b.name||"unknownFunctionName",function(){});c.prototype=b.prototype;c=new c;a=b.apply(c,a);return a instanceof Object?a:c}
|
||||
function Db(a,b){var c=D;if(void 0===c[a].Na){var d=c[a];c[a]=function(){c[a].Na.hasOwnProperty(arguments.length)||X("Function '"+b+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+c[a].Na+")!");return c[a].Na[arguments.length].apply(this,arguments)};c[a].Na=[];c[a].Na[d.yb]=d}}
|
||||
function Eb(a,b,c){D.hasOwnProperty(a)?((void 0===c||void 0!==D[a].Na&&void 0!==D[a].Na[c])&&X("Cannot register public name '"+a+"' twice"),Db(a,a),D.hasOwnProperty(c)&&X("Cannot register multiple overloads of a function with the same number of arguments ("+c+")!"),D[a].Na[c]=b):(D[a]=b,void 0!==c&&(D[a].sc=c))}function Fb(a,b){for(var c=[],d=0;d<a;d++)c.push(A()[(b>>2)+d]);return c}
|
||||
function Gb(a,b){wa(0<=a.indexOf("j"),"getDynCaller should only be called with i64 sigs");var c=[];return function(){c.length=arguments.length;for(var d=0;d<arguments.length;d++)c[d]=arguments[d];return Wa(a,b,c)}}function Hb(a,b){a=W(a);var c=-1!=a.indexOf("j")?Gb(a,b):M.get(b);"function"!==typeof c&&X("unknown function pointer with signature "+a+": "+b);return c}var Ib=void 0;function Jb(a){a=Kb(a);var b=W(a);S(a);return b}
|
||||
function Lb(a,b){function c(g){f[g]||U[g]||(ob[g]?ob[g].forEach(c):(d.push(g),f[g]=!0))}var d=[],f={};b.forEach(c);throw new Ib(a+": "+d.map(Jb).join([", "]));}function Mb(a,b,c){switch(b){case 0:return c?function(d){return e()[d]}:function(d){return v()[d]};case 1:return c?function(d){return x()[d>>1]}:function(d){return ea()[d>>1]};case 2:return c?function(d){return A()[d>>2]}:function(d){return C()[d>>2]};default:throw new TypeError("Unknown integer type: "+a);}}var Nb={};
|
||||
function Ob(){return"object"===typeof globalThis?globalThis:Function("return this")()}function Pb(a,b){var c=U[a];void 0===c&&X(b+" has unknown type "+Jb(a));return c}var Qb={};function Rb(a,b,c){if(0>=a||a>e().length||a&1)return-28;a=Atomics.wait(A(),a>>2,b,c);if("timed-out"===a)return-73;if("not-equal"===a)return-6;if("ok"===a)return 0;throw"Atomics.wait returned an unexpected value "+a;}
|
||||
function Z(a,b){for(var c=arguments.length-2,d=Sb(),f=Tb(8*c),g=f>>3,l=0;l<c;l++)la()[g+l]=arguments[2+l];c=Ub(a,c,f,b);gb(d);return c}var Vb=[],Wb=[],Xb=[0,"undefined"!==typeof document?document:0,"undefined"!==typeof window?window:0];function Yb(a){a=2<a?L(a):a;return Xb[a]||("undefined"!==typeof document?document.querySelector(a):void 0)}
|
||||
function Zb(a,b,c){var d=Yb(a);if(!d)return-4;d.ab&&(A()[d.ab>>2]=b,A()[d.ab+4>>2]=c);if(d.sb||!d.lc)d.sb&&(d=d.sb),a=!1,d.$a&&d.$a.Za&&(a=d.$a.Za.getParameter(2978),a=0===a[0]&&0===a[1]&&a[2]===d.width&&a[3]===d.height),d.width=b,d.height=c,a&&d.$a.Za.viewport(0,0,b,c);else{if(d.ab){d=A()[d.ab+8>>2];a=a?L(a):"";var f=Sb(),g=Tb(12),l=0;if(a){l=Aa(a)+1;var k=R(l);za(a,k,l);l=k}A()[g>>2]=l;A()[g+4>>2]=b;A()[g+8>>2]=c;$b(0,d,657457152,0,l,g);gb(f);return 1}return-4}return 0}
|
||||
function ac(a,b,c){return G?Z(2,1,a,b,c):Zb(a,b,c)}function bc(a){var b=a.getExtension("ANGLE_instanced_arrays");b&&(a.vertexAttribDivisor=function(c,d){b.vertexAttribDivisorANGLE(c,d)},a.drawArraysInstanced=function(c,d,f,g){b.drawArraysInstancedANGLE(c,d,f,g)},a.drawElementsInstanced=function(c,d,f,g,l){b.drawElementsInstancedANGLE(c,d,f,g,l)})}
|
||||
function cc(a){var b=a.getExtension("OES_vertex_array_object");b&&(a.createVertexArray=function(){return b.createVertexArrayOES()},a.deleteVertexArray=function(c){b.deleteVertexArrayOES(c)},a.bindVertexArray=function(c){b.bindVertexArrayOES(c)},a.isVertexArray=function(c){return b.isVertexArrayOES(c)})}function dc(a){var b=a.getExtension("WEBGL_draw_buffers");b&&(a.drawBuffers=function(c,d){b.drawBuffersWEBGL(c,d)})}
|
||||
function ec(a){a||(a=fc);if(!a.Ib){a.Ib=!0;var b=a.Za;bc(b);cc(b);dc(b);b.mc=b.getExtension("EXT_disjoint_timer_query");b.rc=b.getExtension("WEBGL_multi_draw");var c="OES_texture_float OES_texture_half_float OES_standard_derivatives OES_vertex_array_object WEBGL_compressed_texture_s3tc WEBGL_depth_texture OES_element_index_uint EXT_texture_filter_anisotropic EXT_frag_depth WEBGL_draw_buffers ANGLE_instanced_arrays OES_texture_float_linear OES_texture_half_float_linear EXT_blend_minmax EXT_shader_texture_lod EXT_texture_norm16 WEBGL_compressed_texture_pvrtc EXT_color_buffer_half_float WEBGL_color_buffer_float EXT_sRGB WEBGL_compressed_texture_etc1 EXT_disjoint_timer_query WEBGL_compressed_texture_etc WEBGL_compressed_texture_astc EXT_color_buffer_float WEBGL_compressed_texture_s3tc_srgb EXT_disjoint_timer_query_webgl2 WEBKIT_WEBGL_compressed_texture_pvrtc".split(" ");
|
||||
(b.getSupportedExtensions()||[]).forEach(function(d){-1!=c.indexOf(d)&&b.getExtension(d)})}}var fc,gc=["default","low-power","high-performance"],hc={};function ic(){if(!jc){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"===typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:pa||"./this.program"},b;for(b in hc)a[b]=hc[b];var c=[];for(b in a)c.push(b+"="+a[b]);jc=c}return jc}var jc,kc=[null,[],[]];
|
||||
function lc(a){return G?Z(3,1,a):0}function mc(a,b,c,d,f){if(G)return Z(4,1,a,b,c,d,f)}function nc(a,b,c,d){if(G)return Z(5,1,a,b,c,d);for(var f=0,g=0;g<c;g++){for(var l=A()[b+8*g>>2],k=A()[b+(8*g+4)>>2],q=0;q<k;q++){var t=v()[l+q],r=kc[a];0===t||10===t?((1===a?sa:J)(xa(r,0)),r.length=0):r.push(t)}f+=k}A()[d>>2]=f;return 0}
|
||||
function fb(a){if(G)throw"Internal Error! spawnThread() can only ever be called from main application thread!";var b=Q.Cb();if(void 0!==b.La)throw"Internal error!";if(!a.Sa)throw"Internal error, no pthread ptr!";Q.Qa.push(b);for(var c=R(512),d=0;128>d;++d)A()[c+4*d>>2]=0;var f=a.Ra+a.Ta;d=Q.Oa[a.Sa]={worker:b,Ra:a.Ra,Ta:a.Ta,gb:a.gb,wb:a.Sa,threadInfoStruct:a.Sa};var g=d.threadInfoStruct>>2;Atomics.store(C(),g,0);Atomics.store(C(),g+1,0);Atomics.store(C(),g+2,0);Atomics.store(C(),g+17,a.mb);Atomics.store(C(),
|
||||
g+26,c);Atomics.store(C(),g+12,0);Atomics.store(C(),g+10,d.threadInfoStruct);Atomics.store(C(),g+11,42);Atomics.store(C(),g+27,a.Ta);Atomics.store(C(),g+21,a.Ta);Atomics.store(C(),g+20,f);Atomics.store(C(),g+29,f);Atomics.store(C(),g+30,a.mb);Atomics.store(C(),g+32,a.ub);Atomics.store(C(),g+33,a.vb);c=oc()+40;Atomics.store(C(),g+44,c);b.La=d;var l={cmd:"run",start_routine:a.$b,arg:a.Va,threadInfoStruct:a.Sa,selfThreadId:a.Sa,parentThreadId:a.Nb,stackBase:a.Ra,stackSize:a.Ta};b.Xa=function(){l.time=
|
||||
performance.now();b.postMessage(l,a.hc)};b.loaded&&(b.Xa(),delete b.Xa)}function pc(){return P|0}D._pthread_self=pc;
|
||||
function qc(a,b){if(!a)return J("pthread_join attempted on a null thread pointer!"),71;if(G&&selfThreadId==a)return J("PThread "+a+" is attempting to join to itself!"),16;if(!G&&Q.Ka==a)return J("Main thread "+a+" is attempting to join to itself!"),16;if(A()[a+12>>2]!==a)return J("pthread_join attempted on thread "+a+", which does not point to a valid thread, or does not exist anymore!"),71;if(Atomics.load(C(),a+68>>2))return J("Attempted to join thread "+a+", which was already detached!"),28;for(;;){var c=
|
||||
Atomics.load(C(),a+0>>2);if(1==c)return c=Atomics.load(C(),a+4>>2),b&&(A()[b>>2]=c),Atomics.store(C(),a+68>>2,1),G?postMessage({cmd:"cleanupThread",thread:a}):ab(a),0;if(G&&threadInfoStruct&&!Atomics.load(C(),threadInfoStruct+60>>2)&&2==Atomics.load(C(),threadInfoStruct+0>>2))throw"Canceled!";G||eb();Rb(a+0,c,G?100:1)}}function rc(a){return 0===a%4&&(0!==a%100||0===a%400)}function sc(a,b){for(var c=0,d=0;d<=b;c+=a[d++]);return c}
|
||||
var tc=[31,29,31,30,31,30,31,31,30,31,30,31],uc=[31,28,31,30,31,30,31,31,30,31,30,31];function vc(a,b){for(a=new Date(a.getTime());0<b;){var c=a.getMonth(),d=(rc(a.getFullYear())?tc:uc)[c];if(b>d-a.getDate())b-=d-a.getDate()+1,a.setDate(1),11>c?a.setMonth(c+1):(a.setMonth(0),a.setFullYear(a.getFullYear()+1));else{a.setDate(a.getDate()+b);break}}return a}
|
||||
function wc(a,b,c,d){function f(h,p,y){for(h="number"===typeof h?h.toString():h||"";h.length<p;)h=y[0]+h;return h}function g(h,p){return f(h,p,"0")}function l(h,p){function y(I){return 0>I?-1:0<I?1:0}var B;0===(B=y(h.getFullYear()-p.getFullYear()))&&0===(B=y(h.getMonth()-p.getMonth()))&&(B=y(h.getDate()-p.getDate()));return B}function k(h){switch(h.getDay()){case 0:return new Date(h.getFullYear()-1,11,29);case 1:return h;case 2:return new Date(h.getFullYear(),0,3);case 3:return new Date(h.getFullYear(),
|
||||
0,2);case 4:return new Date(h.getFullYear(),0,1);case 5:return new Date(h.getFullYear()-1,11,31);case 6:return new Date(h.getFullYear()-1,11,30)}}function q(h){h=vc(new Date(h.Ja+1900,0,1),h.fb);var p=new Date(h.getFullYear()+1,0,4),y=k(new Date(h.getFullYear(),0,4));p=k(p);return 0>=l(y,h)?0>=l(p,h)?h.getFullYear()+1:h.getFullYear():h.getFullYear()-1}var t=A()[d+40>>2];d={ec:A()[d>>2],dc:A()[d+4>>2],cb:A()[d+8>>2],Ya:A()[d+12>>2],Ua:A()[d+16>>2],Ja:A()[d+20>>2],eb:A()[d+24>>2],fb:A()[d+28>>2],xc:A()[d+
|
||||
32>>2],cc:A()[d+36>>2],fc:t?L(t):""};c=L(c);t={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var r in t)c=c.replace(new RegExp(r,"g"),t[r]);var w="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),
|
||||
z="January February March April May June July August September October November December".split(" ");t={"%a":function(h){return w[h.eb].substring(0,3)},"%A":function(h){return w[h.eb]},"%b":function(h){return z[h.Ua].substring(0,3)},"%B":function(h){return z[h.Ua]},"%C":function(h){return g((h.Ja+1900)/100|0,2)},"%d":function(h){return g(h.Ya,2)},"%e":function(h){return f(h.Ya,2," ")},"%g":function(h){return q(h).toString().substring(2)},"%G":function(h){return q(h)},"%H":function(h){return g(h.cb,
|
||||
2)},"%I":function(h){h=h.cb;0==h?h=12:12<h&&(h-=12);return g(h,2)},"%j":function(h){return g(h.Ya+sc(rc(h.Ja+1900)?tc:uc,h.Ua-1),3)},"%m":function(h){return g(h.Ua+1,2)},"%M":function(h){return g(h.dc,2)},"%n":function(){return"\n"},"%p":function(h){return 0<=h.cb&&12>h.cb?"AM":"PM"},"%S":function(h){return g(h.ec,2)},"%t":function(){return"\t"},"%u":function(h){return h.eb||7},"%U":function(h){var p=new Date(h.Ja+1900,0,1),y=0===p.getDay()?p:vc(p,7-p.getDay());h=new Date(h.Ja+1900,h.Ua,h.Ya);return 0>
|
||||
l(y,h)?g(Math.ceil((31-y.getDate()+(sc(rc(h.getFullYear())?tc:uc,h.getMonth()-1)-31)+h.getDate())/7),2):0===l(y,p)?"01":"00"},"%V":function(h){var p=new Date(h.Ja+1901,0,4),y=k(new Date(h.Ja+1900,0,4));p=k(p);var B=vc(new Date(h.Ja+1900,0,1),h.fb);return 0>l(B,y)?"53":0>=l(p,B)?"01":g(Math.ceil((y.getFullYear()<h.Ja+1900?h.fb+32-y.getDate():h.fb+1-y.getDate())/7),2)},"%w":function(h){return h.eb},"%W":function(h){var p=new Date(h.Ja,0,1),y=1===p.getDay()?p:vc(p,0===p.getDay()?1:7-p.getDay()+1);h=
|
||||
new Date(h.Ja+1900,h.Ua,h.Ya);return 0>l(y,h)?g(Math.ceil((31-y.getDate()+(sc(rc(h.getFullYear())?tc:uc,h.getMonth()-1)-31)+h.getDate())/7),2):0===l(y,p)?"01":"00"},"%y":function(h){return(h.Ja+1900).toString().substring(2)},"%Y":function(h){return h.Ja+1900},"%z":function(h){h=h.cc;var p=0<=h;h=Math.abs(h)/60;return(p?"+":"-")+String("0000"+(h/60*100+h%60)).slice(-4)},"%Z":function(h){return h.fc},"%%":function(){return"%"}};for(r in t)0<=c.indexOf(r)&&(c=c.replace(new RegExp(r,"g"),t[r](d)));r=
|
||||
xc(c);if(r.length>b)return 0;Ha(r,a);return r.length-1}
|
||||
function yc(a){if(G)return Z(6,1,a);switch(a){case 30:return 16384;case 85:return 131072;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:case 79:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;
|
||||
case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1E3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:return"object"===
|
||||
typeof navigator?navigator.hardwareConcurrency||1:1}A()[zc()>>2]=28;return-1}G||Q.Jb();sb=D.InternalError=rb("InternalError");for(var Ac=Array(256),Bc=0;256>Bc;++Bc)Ac[Bc]=String.fromCharCode(Bc);vb=Ac;wb=D.BindingError=rb("BindingError");D.count_emval_handles=function(){for(var a=0,b=5;b<Y.length;++b)void 0!==Y[b]&&++a;return a};D.get_first_emval=function(){for(var a=5;a<Y.length;++a)if(void 0!==Y[a])return Y[a];return null};Ib=D.UnboundTypeError=rb("UnboundTypeError");
|
||||
var Cc=[null,function(a,b){if(G)return Z(1,1,a,b)},ac,lc,mc,nc,yc];function xc(a){var b=Array(Aa(a)+1);ya(a,b,0,b.length);return b}G||Ka.push({Bb:function(){Dc()}});
|
||||
var Gc={i:function(a,b,c,d){K("Assertion failed: "+L(a)+", at: "+[b?L(b):"unknown filename",c,d?L(d):"unknown function"])},K:function(a){return R(a+16)+16},X:function(a,b){return ib(a,b)},I:function(a,b,c){(new jb(a)).Hb(b,c);"uncaught_exception"in kb?kb.xb++:kb.xb=1;throw a;},w:function(a){var b=lb[a];delete lb[a];var c=b.Ob,d=b.Pb,f=b.ob,g=f.map(function(l){return l.Fb}).concat(f.map(function(l){return l.Yb}));tb([a],g,function(l){var k={};f.forEach(function(q,t){var r=l[t],w=q.Db,z=q.Eb,h=l[t+
|
||||
f.length],p=q.Xb,y=q.Zb;k[q.Ab]={read:function(B){return r.fromWireType(w(z,B))},write:function(B,I){var da=[];p(y,B,h.toWireType(da,I));mb(da)}}});return[{name:b.name,fromWireType:function(q){var t={},r;for(r in k)t[r]=k[r].read(q);d(q);return t},toWireType:function(q,t){for(var r in k)if(!(r in t))throw new TypeError('Missing field: "'+r+'"');var w=c();for(r in k)k[r].write(w,t[r]);null!==q&&q.push(d,w);return w},argPackAdvance:8,readValueFromPointer:nb,Pa:d}]})},S:function(a,b,c,d,f){var g=ub(c);
|
||||
b=W(b);V(a,{name:b,fromWireType:function(l){return!!l},toWireType:function(l,k){return k?d:f},argPackAdvance:8,readValueFromPointer:function(l){if(1===c)var k=e();else if(2===c)k=x();else if(4===c)k=A();else throw new TypeError("Unknown boolean type size: "+b);return this.fromWireType(k[l>>g])},Pa:null})},R:function(a,b){b=W(b);V(a,{name:b,fromWireType:function(c){var d=Y[c].value;yb(c);return d},toWireType:function(c,d){return zb(d)},argPackAdvance:8,readValueFromPointer:nb,Pa:null})},t:function(a,
|
||||
b,c){c=ub(c);b=W(b);V(a,{name:b,fromWireType:function(d){return d},toWireType:function(d,f){if("number"!==typeof f&&"boolean"!==typeof f)throw new TypeError('Cannot convert "'+Ab(f)+'" to '+this.name);return f},argPackAdvance:8,readValueFromPointer:Bb(b,c),Pa:null})},v:function(a,b,c,d,f,g){var l=Fb(b,c);a=W(a);f=Hb(d,f);Eb(a,function(){Lb("Cannot call "+a+" due to unbound types",l)},b-1);tb([],l,function(k){var q=a,t=a;k=[k[0],null].concat(k.slice(1));var r=f,w=k.length;2>w&&X("argTypes array size mismatch! Must at least get return value and 'this' types!");
|
||||
for(var z=null!==k[1]&&!1,h=!1,p=1;p<k.length;++p)if(null!==k[p]&&void 0===k[p].Pa){h=!0;break}var y="void"!==k[0].name,B="",I="";for(p=0;p<w-2;++p)B+=(0!==p?", ":"")+"arg"+p,I+=(0!==p?", ":"")+"arg"+p+"Wired";t="return function "+pb(t)+"("+B+") {\nif (arguments.length !== "+(w-2)+") {\nthrowBindingError('function "+t+" called with ' + arguments.length + ' arguments, expected "+(w-2)+" args!');\n}\n";h&&(t+="var destructors = [];\n");var da=h?"destructors":"null";B="throwBindingError invoker fn runDestructors retType classParam".split(" ");
|
||||
r=[X,r,g,mb,k[0],k[1]];z&&(t+="var thisWired = classParam.toWireType("+da+", this);\n");for(p=0;p<w-2;++p)t+="var arg"+p+"Wired = argType"+p+".toWireType("+da+", arg"+p+"); // "+k[p+2].name+"\n",B.push("argType"+p),r.push(k[p+2]);z&&(I="thisWired"+(0<I.length?", ":"")+I);t+=(y?"var rv = ":"")+"invoker(fn"+(0<I.length?", ":"")+I+");\n";if(h)t+="runDestructors(destructors);\n";else for(p=z?1:2;p<k.length;++p)w=1===p?"thisWired":"arg"+(p-2)+"Wired",null!==k[p].Pa&&(t+=w+"_dtor("+w+"); // "+k[p].name+
|
||||
"\n",B.push(w+"_dtor"),r.push(k[p].Pa));y&&(t+="var ret = retType.fromWireType(rv);\nreturn ret;\n");B.push(t+"}\n");k=Cb(B).apply(null,r);p=b-1;if(!D.hasOwnProperty(q))throw new sb("Replacing nonexistant public symbol");void 0!==D[q].Na&&void 0!==p?D[q].Na[p]=k:(D[q]=k,D[q].yb=p);return[]})},j:function(a,b,c,d,f){function g(t){return t}b=W(b);-1===f&&(f=4294967295);var l=ub(c);if(0===d){var k=32-8*c;g=function(t){return t<<k>>>k}}var q=-1!=b.indexOf("unsigned");V(a,{name:b,fromWireType:g,toWireType:function(t,
|
||||
r){if("number"!==typeof r&&"boolean"!==typeof r)throw new TypeError('Cannot convert "'+Ab(r)+'" to '+this.name);if(r<d||r>f)throw new TypeError('Passing a number "'+Ab(r)+'" from JS side to C/C++ side to an argument of type "'+b+'", which is outside the valid range ['+d+", "+f+"]!");return q?r>>>0:r|0},argPackAdvance:8,readValueFromPointer:Mb(b,l,0!==d),Pa:null})},h:function(a,b,c){function d(g){g>>=2;var l=C();return new f(n,l[g+1],l[g])}var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,
|
||||
Uint32Array,Float32Array,Float64Array][b];c=W(c);V(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{Gb:!0})},u:function(a,b){b=W(b);var c="std::string"===b;V(a,{name:b,fromWireType:function(d){var f=C()[d>>2];if(c)for(var g=d+4,l=0;l<=f;++l){var k=d+4+l;if(l==f||0==v()[k]){g=L(g,k-g);if(void 0===q)var q=g;else q+=String.fromCharCode(0),q+=g;g=k+1}}else{q=Array(f);for(l=0;l<f;++l)q[l]=String.fromCharCode(v()[d+4+l]);q=q.join("")}S(d);return q},toWireType:function(d,f){f instanceof
|
||||
ArrayBuffer&&(f=new Uint8Array(f));var g="string"===typeof f;g||f instanceof Uint8Array||f instanceof Uint8ClampedArray||f instanceof Int8Array||X("Cannot pass non-string to std::string");var l=(c&&g?function(){return Aa(f)}:function(){return f.length})(),k=R(4+l+1);C()[k>>2]=l;if(c&&g)za(f,k+4,l+1);else if(g)for(g=0;g<l;++g){var q=f.charCodeAt(g);255<q&&(S(k),X("String has UTF-16 code units that do not fit in 8 bits"));v()[k+4+g]=q}else for(g=0;g<l;++g)v()[k+4+g]=f[g];null!==d&&d.push(S,k);return k},
|
||||
argPackAdvance:8,readValueFromPointer:nb,Pa:function(d){S(d)}})},q:function(a,b,c){c=W(c);if(2===b){var d=Ba;var f=Ca;var g=Da;var l=function(){return ea()};var k=1}else 4===b&&(d=Ea,f=Fa,g=Ga,l=function(){return C()},k=2);V(a,{name:c,fromWireType:function(q){for(var t=C()[q>>2],r=l(),w,z=q+4,h=0;h<=t;++h){var p=q+4+h*b;if(h==t||0==r[p>>k])z=d(z,p-z),void 0===w?w=z:(w+=String.fromCharCode(0),w+=z),z=p+b}S(q);return w},toWireType:function(q,t){"string"!==typeof t&&X("Cannot pass non-string to C++ string type "+
|
||||
c);var r=g(t),w=R(4+r+b);C()[w>>2]=r>>k;f(t,w+4,r+b);null!==q&&q.push(S,w);return w},argPackAdvance:8,readValueFromPointer:nb,Pa:function(q){S(q)}})},x:function(a,b,c,d,f,g){lb[a]={name:W(b),Ob:Hb(c,d),Pb:Hb(f,g),ob:[]}},l:function(a,b,c,d,f,g,l,k,q,t){lb[a].ob.push({Ab:W(b),Fb:c,Db:Hb(d,f),Eb:g,Yb:l,Xb:Hb(k,q),Zb:t})},T:function(a,b){b=W(b);V(a,{oc:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},H:function(a,b){if(a==b)postMessage({cmd:"processQueuedMainThreadWork"});
|
||||
else if(G)postMessage({targetThread:a,cmd:"processThreadQueue"});else{a=(a=Q.Oa[a])&&a.worker;if(!a)return;a.postMessage({cmd:"processThreadQueue"})}return 1},m:yb,W:function(a){if(0===a)return zb(Ob());var b=Nb[a];a=void 0===b?W(a):b;return zb(Ob()[a])},V:function(a){4<a&&(Y[a].jb+=1)},y:function(a,b,c,d){a||X("Cannot use deleted val. handle = "+a);a=Y[a].value;var f=Qb[b];if(!f){f="";for(var g=0;g<b;++g)f+=(0!==g?", ":"")+"arg"+g;var l="return function emval_allocator_"+b+"(constructor, argTypes, args) {\n";
|
||||
for(g=0;g<b;++g)l+="var argType"+g+" = requireRegisteredType(Module['HEAP32'][(argTypes >>> 2) + "+g+'], "parameter '+g+'");\nvar arg'+g+" = argType"+g+".readValueFromPointer(args);\nargs += argType"+g+"['argPackAdvance'];\n";f=(new Function("requireRegisteredType","Module","__emval_register",l+("var obj = new constructor("+f+");\nreturn __emval_register(obj);\n}\n")))(Pb,D,zb);Qb[b]=f}return f(a,c,d)},b:function(){K()},n:function(a,b,c){Wb.length=0;var d;for(c>>=2;d=v()[b++];)(d=105>d)&&c&1&&c++,
|
||||
Wb.push(d?la()[c++>>1]:A()[c]),++c;return Ua[a].apply(null,Wb)},J:function(){},r:function(){},f:Rb,g:$a,d:hb,p:function(){return Ya|0},o:function(){return Xa|0},C:function(a,b,c){v().copyWithin(a,b,b+c)},E:function(a,b,c){Vb.length=b;c>>=3;for(var d=0;d<b;d++)Vb[d]=la()[c+d];return(0>a?Ua[-a-1]:Cc[a]).apply(null,Vb)},k:function(a){a>>>=0;var b=v().length;if(a<=b||2147483648<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);d=Math.max(16777216,a,d);0<d%65536&&(d+=65536-d%
|
||||
65536);a:{try{m.grow(Math.min(2147483648,d)-n.byteLength+65535>>>16);u(m.buffer);var f=1;break a}catch(g){}f=void 0}if(f)return!0}return!1},F:function(a,b,c){return Yb(a)?Zb(a,b,c):ac(a,b,c)},e:function(){},G:function(a,b){var c={};b>>=2;c.alpha=!!A()[b];c.depth=!!A()[b+1];c.stencil=!!A()[b+2];c.antialias=!!A()[b+3];c.premultipliedAlpha=!!A()[b+4];c.preserveDrawingBuffer=!!A()[b+5];var d=A()[b+6];c.powerPreference=gc[d];c.failIfMajorPerformanceCaveat=!!A()[b+7];c.Mb=A()[b+8];c.qc=A()[b+9];c.nb=A()[b+
|
||||
10];c.zb=A()[b+11];c.tc=A()[b+12];c.uc=A()[b+13];a=Yb(a);!a||c.zb?c=0:(a=a.getContext("webgl",c))?(b=R(8),A()[b+4>>2]=P|0,d={nc:b,attributes:c,version:c.Mb,Za:a},a.canvas&&(a.canvas.$a=d),("undefined"===typeof c.nb||c.nb)&&ec(d),c=b):c=0;return c},O:function(a,b){var c=0;ic().forEach(function(d,f){var g=b+c;f=A()[a+4*f>>2]=g;for(g=0;g<d.length;++g)e()[f++>>0]=d.charCodeAt(g);e()[f>>0]=0;c+=d.length+1});return 0},P:function(a,b){var c=ic();A()[a>>2]=c.length;var d=0;c.forEach(function(f){d+=f.length+
|
||||
1});A()[b>>2]=d;return 0},Q:lc,z:mc,s:nc,B:function(){Q.Kb()},a:m||D.wasmMemory,D:ib,U:function(a,b,c,d){if("undefined"===typeof SharedArrayBuffer)return J("Current environment does not support SharedArrayBuffer, pthreads are not available!"),6;if(!a)return J("pthread_create called with a null thread pointer!"),28;var f=[];if(G&&0===f.length)return Ec(687865856,a,b,c,d);var g=0,l=0,k=0,q=0;if(b){var t=A()[b>>2];t+=81920;g=A()[b+8>>2];l=0!==A()[b+12>>2];if(0===A()[b+16>>2]){var r=A()[b+20>>2],w=A()[b+
|
||||
24>>2];k=b+20;q=b+24;var z=Q.hb?Q.hb:P|0;if(k||q)if(z)if(A()[z+12>>2]!==z)J("pthread_getschedparam attempted on thread "+z+", which does not point to a valid thread, or does not exist anymore!");else{var h=Atomics.load(C(),z+108+20>>2);z=Atomics.load(C(),z+108+24>>2);k&&(A()[k>>2]=h);q&&(A()[q>>2]=z)}else J("pthread_getschedparam called with a null thread pointer!");k=A()[b+20>>2];q=A()[b+24>>2];A()[b+20>>2]=r;A()[b+24>>2]=w}else k=A()[b+20>>2],q=A()[b+24>>2]}else t=2097152;(b=0==g)?g=Fc(16,t):(g-=
|
||||
t,wa(0<g));r=R(232);for(w=0;58>w;++w)C()[(r>>2)+w]=0;A()[a>>2]=r;A()[r+12>>2]=r;a=r+156;A()[a>>2]=a;c={Ra:g,Ta:t,gb:b,ub:k,vb:q,mb:l,$b:c,Sa:r,Nb:P|0,Va:d,hc:f};G?(c.kc="spawnThread",postMessage(c,f)):fb(c);return 0},L:function(a,b){return qc(a,b)},c:pc,A:function(){},N:function(a,b,c,d){return wc(a,b,c,d)},M:yc};
|
||||
(function(){function a(f,g){D.asm=f.exports;M=D.asm.Y;ua=g;if(!G){var l=Q.Ma.length;Q.Ma.forEach(function(k){Q.qb(k,function(){if(!--l&&(N--,D.monitorRunDependencies&&D.monitorRunDependencies(N),0==N&&(null!==Oa&&(clearInterval(Oa),Oa=null),Pa))){var q=Pa;Pa=null;q()}})})}}function b(f){a(f.instance,f.module)}function c(f){return Sa().then(function(g){return WebAssembly.instantiate(g,d)}).then(f,function(g){J("failed to asynchronously prepare wasm: "+g);K(g)})}var d={a:Gc};G||(wa(!G,"addRunDependency cannot be used in a pthread worker"),
|
||||
N++,D.monitorRunDependencies&&D.monitorRunDependencies(N));if(D.instantiateWasm)try{return D.instantiateWasm(d,a)}catch(f){return J("Module.instantiateWasm callback failed with error: "+f),!1}(function(){return ta||"function"!==typeof WebAssembly.instantiateStreaming||Qa()||"function"!==typeof fetch?c(b):fetch(O,{credentials:"same-origin"}).then(function(f){return WebAssembly.instantiateStreaming(f,d).then(b,function(g){J("wasm streaming compile failed: "+g);J("falling back to ArrayBuffer instantiation");
|
||||
return c(b)})})})().catch(oa);return{}})();var Dc=D.___wasm_call_ctors=function(){return(Dc=D.___wasm_call_ctors=D.asm.Z).apply(null,arguments)},R=D._malloc=function(){return(R=D._malloc=D.asm._).apply(null,arguments)},S=D._free=function(){return(S=D._free=D.asm.$).apply(null,arguments)},zc=D.___errno_location=function(){return(zc=D.___errno_location=D.asm.aa).apply(null,arguments)},Kb=D.___getTypeName=function(){return(Kb=D.___getTypeName=D.asm.ba).apply(null,arguments)};
|
||||
D.___embind_register_native_and_builtin_types=function(){return(D.___embind_register_native_and_builtin_types=D.asm.ca).apply(null,arguments)};D.___em_js__initPthreadsJS=function(){return(D.___em_js__initPthreadsJS=D.asm.da).apply(null,arguments)};
|
||||
var oc=D._emscripten_get_global_libc=function(){return(oc=D._emscripten_get_global_libc=D.asm.ea).apply(null,arguments)},Sb=D.stackSave=function(){return(Sb=D.stackSave=D.asm.fa).apply(null,arguments)},gb=D.stackRestore=function(){return(gb=D.stackRestore=D.asm.ga).apply(null,arguments)},Tb=D.stackAlloc=function(){return(Tb=D.stackAlloc=D.asm.ha).apply(null,arguments)},Fc=D._memalign=function(){return(Fc=D._memalign=D.asm.ia).apply(null,arguments)};
|
||||
D._emscripten_main_browser_thread_id=function(){return(D._emscripten_main_browser_thread_id=D.asm.ja).apply(null,arguments)};var db=D.___pthread_tsd_run_dtors=function(){return(db=D.___pthread_tsd_run_dtors=D.asm.ka).apply(null,arguments)},eb=D._emscripten_main_thread_process_queued_calls=function(){return(eb=D._emscripten_main_thread_process_queued_calls=D.asm.la).apply(null,arguments)};
|
||||
D._emscripten_current_thread_process_queued_calls=function(){return(D._emscripten_current_thread_process_queued_calls=D.asm.ma).apply(null,arguments)};var bb=D._emscripten_register_main_browser_thread_id=function(){return(bb=D._emscripten_register_main_browser_thread_id=D.asm.na).apply(null,arguments)},Ta=D._do_emscripten_dispatch_to_thread=function(){return(Ta=D._do_emscripten_dispatch_to_thread=D.asm.oa).apply(null,arguments)};
|
||||
D._emscripten_async_run_in_main_thread=function(){return(D._emscripten_async_run_in_main_thread=D.asm.pa).apply(null,arguments)};D._emscripten_sync_run_in_main_thread=function(){return(D._emscripten_sync_run_in_main_thread=D.asm.qa).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_0=function(){return(D._emscripten_sync_run_in_main_thread_0=D.asm.ra).apply(null,arguments)};
|
||||
D._emscripten_sync_run_in_main_thread_1=function(){return(D._emscripten_sync_run_in_main_thread_1=D.asm.sa).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_2=function(){return(D._emscripten_sync_run_in_main_thread_2=D.asm.ta).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_xprintf_varargs=function(){return(D._emscripten_sync_run_in_main_thread_xprintf_varargs=D.asm.ua).apply(null,arguments)};
|
||||
D._emscripten_sync_run_in_main_thread_3=function(){return(D._emscripten_sync_run_in_main_thread_3=D.asm.va).apply(null,arguments)};var Ec=D._emscripten_sync_run_in_main_thread_4=function(){return(Ec=D._emscripten_sync_run_in_main_thread_4=D.asm.wa).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_5=function(){return(D._emscripten_sync_run_in_main_thread_5=D.asm.xa).apply(null,arguments)};
|
||||
D._emscripten_sync_run_in_main_thread_6=function(){return(D._emscripten_sync_run_in_main_thread_6=D.asm.ya).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_7=function(){return(D._emscripten_sync_run_in_main_thread_7=D.asm.za).apply(null,arguments)};
|
||||
var Ub=D._emscripten_run_in_main_runtime_thread_js=function(){return(Ub=D._emscripten_run_in_main_runtime_thread_js=D.asm.Aa).apply(null,arguments)},$b=D.__emscripten_call_on_thread=function(){return($b=D.__emscripten_call_on_thread=D.asm.Ba).apply(null,arguments)};D._emscripten_tls_init=function(){return(D._emscripten_tls_init=D.asm.Ca).apply(null,arguments)};D.dynCall_viijii=function(){return(D.dynCall_viijii=D.asm.Da).apply(null,arguments)};
|
||||
D.dynCall_iiji=function(){return(D.dynCall_iiji=D.asm.Ea).apply(null,arguments)};D.dynCall_jiji=function(){return(D.dynCall_jiji=D.asm.Fa).apply(null,arguments)};D.dynCall_iiiiiijj=function(){return(D.dynCall_iiiiiijj=D.asm.Ga).apply(null,arguments)};D.dynCall_iiiiij=function(){return(D.dynCall_iiiiij=D.asm.Ha).apply(null,arguments)};D.dynCall_iiiiijj=function(){return(D.dynCall_iiiiijj=D.asm.Ia).apply(null,arguments)};var cb=D._main_thread_futex=3067912;D.PThread=Q;D.PThread=Q;D._pthread_self=pc;
|
||||
D.wasmMemory=m;D.ExitStatus=Hc;var Ic;function Hc(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}Pa=function Jc(){Ic||Kc();Ic||(Pa=Jc)};
|
||||
function Kc(){function a(){if(!Ic&&(Ic=!0,D.calledRun=!0,!va)){Va(Ka);G||Va(La);na(D);if(D.onRuntimeInitialized)D.onRuntimeInitialized();if(!G){if(D.postRun)for("function"==typeof D.postRun&&(D.postRun=[D.postRun]);D.postRun.length;){var b=D.postRun.shift();Ma.unshift(b)}Va(Ma)}}}if(!(0<N)){if(!G){if(D.preRun)for("function"==typeof D.preRun&&(D.preRun=[D.preRun]);D.preRun.length;)Na();Va(Ja)}0<N||(D.setStatus?(D.setStatus("Running..."),setTimeout(function(){setTimeout(function(){D.setStatus("")},
|
||||
1);a()},1)):a())}}D.run=Kc;if(D.preInit)for("function"==typeof D.preInit&&(D.preInit=[D.preInit]);0<D.preInit.length;)D.preInit.pop()();G||(noExitRuntime=!0);G?Q.Lb():Kc();
|
||||
|
||||
|
||||
return jxl_enc_mt.ready
|
||||
}
|
||||
);
|
||||
})();
|
||||
export default jxl_enc_mt;
|
||||
1
codecs/jxl/enc/jxl_enc_mt.worker.js
generated
@@ -1 +0,0 @@
|
||||
var threadInfoStruct=0;var selfThreadId=0;var parentThreadId=0;var initializedJS=false;var Module={};function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:selfThreadId})}var err=threadPrintErr;this.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);Module["wasmModule"]=null;receiveInstance(instance);return instance.exports};this.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;import(e.data.urlOrBlob).then(function(jxl_enc_mt){return jxl_enc_mt.default(Module)}).then(function(instance){Module=instance;postMessage({"cmd":"loaded"})})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;threadInfoStruct=e.data.threadInfoStruct;Module["registerPthreadPtr"](threadInfoStruct,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/0);selfThreadId=e.data.selfThreadId;parentThreadId=e.data.parentThreadId;var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["_emscripten_tls_init"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].setThreadStatus(Module["_pthread_self"](),1);if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["dynCall"]("ii",e.data.start_routine,[e.data.arg]);if(!Module["getNoExitRuntime"]())Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){Atomics.store(Module["HEAPU32"],(threadInfoStruct+4)>>/*C_STRUCTS.pthread.threadExitCode*/2,(ex instanceof Module["ExitStatus"])?ex.status:-2);/*A custom entry specific to Emscripten denoting that the thread crashed.*/Atomics.store(Module["HEAPU32"],(threadInfoStruct+0)>>/*C_STRUCTS.pthread.threadStatus*/2,1);Module["_emscripten_futex_wake"](threadInfoStruct+0,/*C_STRUCTS.pthread.threadStatus*/2147483647);if(!(ex instanceof Module["ExitStatus"]))throw ex}}}else if(e.data.cmd==="cancel"){if(threadInfoStruct){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(threadInfoStruct){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}};
|
||||
1
codecs/jxl/enc/jxl_enc_mt_simd.d.ts
vendored
@@ -1 +0,0 @@
|
||||
export { default } from './jxl_enc';
|
||||
112
codecs/jxl/enc/jxl_enc_mt_simd.js
generated
@@ -1,112 +0,0 @@
|
||||
|
||||
var jxl_enc_mt_simd = (function() {
|
||||
var _scriptDir = import.meta.url;
|
||||
|
||||
return (
|
||||
function(jxl_enc_mt_simd) {
|
||||
jxl_enc_mt_simd = jxl_enc_mt_simd || {};
|
||||
|
||||
|
||||
function e(){m.buffer!=n&&u(m.buffer);return aa}function v(){m.buffer!=n&&u(m.buffer);return ba}function x(){m.buffer!=n&&u(m.buffer);return ca}function ea(){m.buffer!=n&&u(m.buffer);return fa}function A(){m.buffer!=n&&u(m.buffer);return ha}function C(){m.buffer!=n&&u(m.buffer);return ia}function ja(){m.buffer!=n&&u(m.buffer);return ka}function la(){m.buffer!=n&&u(m.buffer);return ma}var D;D||(D=typeof jxl_enc_mt_simd !== 'undefined' ? jxl_enc_mt_simd : {});var na,oa;
|
||||
D.ready=new Promise(function(a,b){na=a;oa=b});var E={},F;for(F in D)D.hasOwnProperty(F)&&(E[F]=D[F]);var pa="./this.program",G=D.ENVIRONMENT_IS_PTHREAD||!1;G&&(n=D.buffer);var H="";function qa(a){return D.locateFile?D.locateFile(a,H):H+a}var ra;H=self.location.href;_scriptDir&&(H=_scriptDir);0!==H.indexOf("blob:")?H=H.substr(0,H.lastIndexOf("/")+1):H="";ra=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)};
|
||||
var sa=D.print||console.log.bind(console),J=D.printErr||console.warn.bind(console);for(F in E)E.hasOwnProperty(F)&&(D[F]=E[F]);E=null;D.thisProgram&&(pa=D.thisProgram);var ta;D.wasmBinary&&(ta=D.wasmBinary);var noExitRuntime;D.noExitRuntime&&(noExitRuntime=D.noExitRuntime);"object"!==typeof WebAssembly&&K("no native wasm support detected");var m,ua,threadInfoStruct=0,selfThreadId=0,va=!1;function wa(a,b){a||K("Assertion failed: "+b)}
|
||||
function xa(a,b,c){c=b+c;for(var d="";!(b>=c);){var f=a[b++];if(!f)break;if(f&128){var g=a[b++]&63;if(192==(f&224))d+=String.fromCharCode((f&31)<<6|g);else{var l=a[b++]&63;f=224==(f&240)?(f&15)<<12|g<<6|l:(f&7)<<18|g<<12|l<<6|a[b++]&63;65536>f?d+=String.fromCharCode(f):(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023))}}else d+=String.fromCharCode(f)}return d}function L(a,b){return a?xa(v(),a,b):""}
|
||||
function ya(a,b,c,d){if(0<d){d=c+d-1;for(var f=0;f<a.length;++f){var g=a.charCodeAt(f);if(55296<=g&&57343>=g){var l=a.charCodeAt(++f);g=65536+((g&1023)<<10)|l&1023}if(127>=g){if(c>=d)break;b[c++]=g}else{if(2047>=g){if(c+1>=d)break;b[c++]=192|g>>6}else{if(65535>=g){if(c+2>=d)break;b[c++]=224|g>>12}else{if(c+3>=d)break;b[c++]=240|g>>18;b[c++]=128|g>>12&63}b[c++]=128|g>>6&63}b[c++]=128|g&63}}b[c]=0}}function za(a,b,c){ya(a,v(),b,c)}
|
||||
function Aa(a){for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);55296<=d&&57343>=d&&(d=65536+((d&1023)<<10)|a.charCodeAt(++c)&1023);127>=d?++b:b=2047>=d?b+2:65535>=d?b+3:b+4}return b}function Ba(a,b){for(var c=0,d="";;){var f=x()[a+2*c>>1];if(0==f||c==b/2)return d;++c;d+=String.fromCharCode(f)}}function Ca(a,b,c){void 0===c&&(c=2147483647);if(2>c)return 0;c-=2;var d=b;c=c<2*a.length?c/2:a.length;for(var f=0;f<c;++f){var g=a.charCodeAt(f);x()[b>>1]=g;b+=2}x()[b>>1]=0;return b-d}
|
||||
function Da(a){return 2*a.length}function Ea(a,b){for(var c=0,d="";!(c>=b/4);){var f=A()[a+4*c>>2];if(0==f)break;++c;65536<=f?(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023)):d+=String.fromCharCode(f)}return d}function Fa(a,b,c){void 0===c&&(c=2147483647);if(4>c)return 0;var d=b;c=d+c-4;for(var f=0;f<a.length;++f){var g=a.charCodeAt(f);if(55296<=g&&57343>=g){var l=a.charCodeAt(++f);g=65536+((g&1023)<<10)|l&1023}A()[b>>2]=g;b+=4;if(b+4>c)break}A()[b>>2]=0;return b-d}
|
||||
function Ga(a){for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);55296<=d&&57343>=d&&++c;b+=4}return b}function Ha(a,b){e().set(a,b)}var n,aa,ba,ca,fa,ha,ia,ka,ma;function u(a){n=a;D.HEAP8=aa=new Int8Array(a);D.HEAP16=ca=new Int16Array(a);D.HEAP32=ha=new Int32Array(a);D.HEAPU8=ba=new Uint8Array(a);D.HEAPU16=fa=new Uint16Array(a);D.HEAPU32=ia=new Uint32Array(a);D.HEAPF32=ka=new Float32Array(a);D.HEAPF64=ma=new Float64Array(a)}var Ia=D.INITIAL_MEMORY||16777216;
|
||||
if(G)m=D.wasmMemory,n=D.buffer;else if(D.wasmMemory)m=D.wasmMemory;else if(m=new WebAssembly.Memory({initial:Ia/65536,maximum:32768,shared:!0}),!(m.buffer instanceof SharedArrayBuffer))throw J("requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag"),Error("bad memory");m&&(n=m.buffer);Ia=n.byteLength;u(n);var M,Ja=[],Ka=[],La=[],Ma=[];
|
||||
function Na(){var a=D.preRun.shift();Ja.unshift(a)}var N=0,Oa=null,Pa=null;D.preloadedImages={};D.preloadedAudios={};function K(a){if(D.onAbort)D.onAbort(a);G&&console.error("Pthread aborting at "+Error().stack);J(a);va=!0;a=new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");oa(a);throw a;}function Qa(){var a=O;return String.prototype.startsWith?a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}var O="jxl_enc_mt_simd.wasm";
|
||||
Qa()||(O=qa(O));function Ra(){try{if(ta)return new Uint8Array(ta);if(ra)return ra(O);throw"both async and sync fetching of the wasm failed";}catch(a){K(a)}}function Sa(){return ta||"function"!==typeof fetch?Promise.resolve().then(Ra):fetch(O,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+O+"'";return a.arrayBuffer()}).catch(function(){return Ra()})}var Ua={60053:function(a,b){setTimeout(function(){Ta(a,b)},0)},60131:function(){throw"Canceled!";}};
|
||||
function Va(a){for(;0<a.length;){var b=a.shift();if("function"==typeof b)b(D);else{var c=b.Bb;"number"===typeof c?void 0===b.Va?M.get(c)():M.get(c)(b.Va):c(void 0===b.Va?null:b.Va)}}}function Wa(a,b,c){var d;-1!=a.indexOf("j")?d=c&&c.length?D["dynCall_"+a].apply(null,[b].concat(c)):D["dynCall_"+a].call(null,b):d=M.get(b).apply(null,c);return d}D.dynCall=Wa;var P=0,Xa=0,Ya=0;function Za(a,b,c){P=a|0;Ya=b|0;Xa=c|0}D.registerPthreadPtr=Za;
|
||||
function $a(a,b){if(0>=a||a>e().length||a&1||0>b)return-28;if(0==b)return 0;2147483647<=b&&(b=Infinity);var c=Atomics.load(A(),Q.rb>>2),d=0;if(c==a&&Atomics.compareExchange(A(),Q.rb>>2,c,0)==c&&(--b,d=1,0>=b))return 1;a=Atomics.notify(A(),a>>2,b);if(0<=a)return a+d;throw"Atomics.notify returned an unexpected value "+a;}D._emscripten_futex_wake=$a;
|
||||
function ab(a){if(G)throw"Internal Error! cleanupThread() can only ever be called from main application thread!";if(!a)throw"Internal Error! Null pthread_ptr in cleanupThread!";A()[a+12>>2]=0;(a=Q.Oa[a])&&Q.bb(a.worker)}
|
||||
var Q={ic:1,pc:{ub:0,vb:0},Ma:[],Qa:[],Jb:function(){for(var a=navigator.hardwareConcurrency,b=0;b<a;++b)Q.lb()},Kb:function(){Q.Ka=R(232);for(var a=0;58>a;++a)C()[Q.Ka/4+a]=0;A()[Q.Ka+12>>2]=Q.Ka;a=Q.Ka+156;A()[a>>2]=a;var b=R(512);for(a=0;128>a;++a)C()[b/4+a]=0;Atomics.store(C(),Q.Ka+104>>2,b);Atomics.store(C(),Q.Ka+40>>2,Q.Ka);Atomics.store(C(),Q.Ka+44>>2,42);Q.pb();Za(Q.Ka,!1,1);bb(Q.Ka)},Lb:function(){Q.pb();na(D);Q.receiveObjectTransfer=Q.Qb;Q.setThreadStatus=Q.Rb;Q.threadCancel=Q.ac;Q.threadExit=
|
||||
Q.bc},pb:function(){Q.rb=cb},Oa:{},kb:[],Rb:function(){},tb:function(){for(;0<Q.kb.length;)Q.kb.pop()();G&&threadInfoStruct&&db()},bc:function(a){var b=P|0;b&&(Atomics.store(C(),b+4>>2,a),Atomics.store(C(),b+0>>2,1),Atomics.store(C(),b+60>>2,1),Atomics.store(C(),b+64>>2,0),Q.tb(),$a(b+0,2147483647),Za(0,0,0),threadInfoStruct=0,G&&postMessage({cmd:"exit"}))},ac:function(){Q.tb();Atomics.store(C(),threadInfoStruct+4>>2,-1);Atomics.store(C(),threadInfoStruct+0>>2,1);$a(threadInfoStruct+0,2147483647);
|
||||
threadInfoStruct=selfThreadId=0;Za(0,0,0);postMessage({cmd:"cancelDone"})},wc:function(){for(var a in Q.Oa){var b=Q.Oa[a];b&&b.worker&&Q.bb(b.worker)}Q.Oa={};for(a=0;a<Q.Ma.length;++a){var c=Q.Ma[a];c.terminate()}Q.Ma=[];for(a=0;a<Q.Qa.length;++a)c=Q.Qa[a],b=c.La,Q.ib(b),c.terminate();Q.Qa=[]},ib:function(a){if(a){if(a.threadInfoStruct){var b=A()[a.threadInfoStruct+104>>2];A()[a.threadInfoStruct+104>>2]=0;S(b);S(a.threadInfoStruct)}a.threadInfoStruct=0;a.gb&&a.Ra&&S(a.Ra);a.Ra=0;a.worker&&(a.worker.La=
|
||||
null)}},bb:function(a){delete Q.Oa[a.La.wb];Q.Ma.push(a);Q.Qa.splice(Q.Qa.indexOf(a),1);Q.ib(a.La);a.La=void 0},Qb:function(){},qb:function(a,b){a.onmessage=function(c){var d=c.data,f=d.cmd;a.La&&(Q.hb=a.La.threadInfoStruct);if(d.targetThread&&d.targetThread!=(P|0)){var g=Q.Oa[d.vc];g?g.worker.postMessage(c.data,d.transferList):console.error('Internal error! Worker sent a message "'+f+'" to target pthread '+d.targetThread+", but that thread no longer exists!")}else if("processQueuedMainThreadWork"===
|
||||
f)eb();else if("spawnThread"===f)fb(c.data);else if("cleanupThread"===f)ab(d.thread);else if("killThread"===f){c=d.thread;if(G)throw"Internal Error! killThread() can only ever be called from main application thread!";if(!c)throw"Internal Error! Null pthread_ptr in killThread!";A()[c+12>>2]=0;c=Q.Oa[c];c.worker.terminate();Q.ib(c);Q.Qa.splice(Q.Qa.indexOf(c.worker),1);c.worker.La=void 0}else if("cancelThread"===f){c=d.thread;if(G)throw"Internal Error! cancelThread() can only ever be called from main application thread!";
|
||||
if(!c)throw"Internal Error! Null pthread_ptr in cancelThread!";Q.Oa[c].worker.postMessage({cmd:"cancel"})}else"loaded"===f?(a.loaded=!0,b&&b(a),a.Xa&&(a.Xa(),delete a.Xa)):"print"===f?sa("Thread "+d.threadId+": "+d.text):"printErr"===f?J("Thread "+d.threadId+": "+d.text):"alert"===f?alert("Thread "+d.threadId+": "+d.text):"exit"===f?a.La&&Atomics.load(C(),a.La.wb+68>>2)&&Q.bb(a):"cancelDone"===f?Q.bb(a):"objectTransfer"!==f&&("setimmediate"===c.data.target?a.postMessage(c.data):J("worker sent an unknown command "+
|
||||
f));Q.hb=void 0};a.onerror=function(c){J("pthread sent an error! "+c.filename+":"+c.lineno+": "+c.message)};a.postMessage({cmd:"load",urlOrBlob:D.mainScriptUrlOrBlob||_scriptDir,wasmMemory:m,wasmModule:ua})},lb:function(){var a=qa("jxl_enc_mt_simd.worker.js");Q.Ma.push(new Worker(a))},Cb:function(){0==Q.Ma.length&&(Q.lb(),Q.qb(Q.Ma[0]));return 0<Q.Ma.length?Q.Ma.pop():null},jc:function(a){for(a=performance.now()+a;performance.now()<a;);}};D.establishStackSpace=function(a){gb(a)};
|
||||
D.getNoExitRuntime=function(){return noExitRuntime};var hb;hb=G?function(){return performance.now()-D.__performance_now_clock_drift}:function(){return performance.now()};function ib(a,b){Q.kb.push(function(){M.get(a)(b)})}
|
||||
function jb(a){this.Wa=a-16;this.Wb=function(b){A()[this.Wa+8>>2]=b};this.Tb=function(b){A()[this.Wa+0>>2]=b};this.Ub=function(){A()[this.Wa+4>>2]=0};this.Sb=function(){var b=0;e()[this.Wa+12>>0]=b};this.Vb=function(){var b=0;e()[this.Wa+13>>0]=b};this.Hb=function(b,c){this.Wb(b);this.Tb(c);this.Ub();this.Sb();this.Vb()}}function kb(){return 0<kb.xb}var lb={};function mb(a){for(;a.length;){var b=a.pop();a.pop()(b)}}function nb(a){return this.fromWireType(C()[a>>2])}var T={},U={},ob={};
|
||||
function pb(a){if(void 0===a)return"_unknown";a=a.replace(/[^a-zA-Z0-9_]/g,"$");var b=a.charCodeAt(0);return 48<=b&&57>=b?"_"+a:a}function qb(a,b){a=pb(a);return(new Function("body","return function "+a+'() {\n "use strict"; return body.apply(this, arguments);\n};\n'))(b)}
|
||||
function rb(a){var b=Error,c=qb(a,function(d){this.name=a;this.message=d;d=Error(d).stack;void 0!==d&&(this.stack=this.toString()+"\n"+d.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(b.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message};return c}var sb=void 0;
|
||||
function tb(a,b,c){function d(k){k=c(k);if(k.length!==a.length)throw new sb("Mismatched type converter count");for(var q=0;q<a.length;++q)V(a[q],k[q])}a.forEach(function(k){ob[k]=b});var f=Array(b.length),g=[],l=0;b.forEach(function(k,q){U.hasOwnProperty(k)?f[q]=U[k]:(g.push(k),T.hasOwnProperty(k)||(T[k]=[]),T[k].push(function(){f[q]=U[k];++l;l===g.length&&d(f)}))});0===g.length&&d(f)}
|
||||
function ub(a){switch(a){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+a);}}var vb=void 0;function W(a){for(var b="";v()[a];)b+=vb[v()[a++]];return b}var wb=void 0;function X(a){throw new wb(a);}
|
||||
function V(a,b,c){c=c||{};if(!("argPackAdvance"in b))throw new TypeError("registerType registeredInstance requires argPackAdvance");var d=b.name;a||X('type "'+d+'" must have a positive integer typeid pointer');if(U.hasOwnProperty(a)){if(c.Gb)return;X("Cannot register type '"+d+"' twice")}U[a]=b;delete ob[a];T.hasOwnProperty(a)&&(b=T[a],delete T[a],b.forEach(function(f){f()}))}var xb=[],Y=[{},{value:void 0},{value:null},{value:!0},{value:!1}];
|
||||
function yb(a){4<a&&0===--Y[a].jb&&(Y[a]=void 0,xb.push(a))}function zb(a){switch(a){case void 0:return 1;case null:return 2;case !0:return 3;case !1:return 4;default:var b=xb.length?xb.pop():Y.length;Y[b]={jb:1,value:a};return b}}function Ab(a){if(null===a)return"null";var b=typeof a;return"object"===b||"array"===b||"function"===b?a.toString():""+a}
|
||||
function Bb(a,b){switch(b){case 2:return function(c){return this.fromWireType(ja()[c>>2])};case 3:return function(c){return this.fromWireType(la()[c>>3])};default:throw new TypeError("Unknown float type: "+a);}}function Cb(a){var b=Function;if(!(b instanceof Function))throw new TypeError("new_ called with constructor type "+typeof b+" which is not a function");var c=qb(b.name||"unknownFunctionName",function(){});c.prototype=b.prototype;c=new c;a=b.apply(c,a);return a instanceof Object?a:c}
|
||||
function Db(a,b){var c=D;if(void 0===c[a].Na){var d=c[a];c[a]=function(){c[a].Na.hasOwnProperty(arguments.length)||X("Function '"+b+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+c[a].Na+")!");return c[a].Na[arguments.length].apply(this,arguments)};c[a].Na=[];c[a].Na[d.yb]=d}}
|
||||
function Eb(a,b,c){D.hasOwnProperty(a)?((void 0===c||void 0!==D[a].Na&&void 0!==D[a].Na[c])&&X("Cannot register public name '"+a+"' twice"),Db(a,a),D.hasOwnProperty(c)&&X("Cannot register multiple overloads of a function with the same number of arguments ("+c+")!"),D[a].Na[c]=b):(D[a]=b,void 0!==c&&(D[a].sc=c))}function Fb(a,b){for(var c=[],d=0;d<a;d++)c.push(A()[(b>>2)+d]);return c}
|
||||
function Gb(a,b){wa(0<=a.indexOf("j"),"getDynCaller should only be called with i64 sigs");var c=[];return function(){c.length=arguments.length;for(var d=0;d<arguments.length;d++)c[d]=arguments[d];return Wa(a,b,c)}}function Hb(a,b){a=W(a);var c=-1!=a.indexOf("j")?Gb(a,b):M.get(b);"function"!==typeof c&&X("unknown function pointer with signature "+a+": "+b);return c}var Ib=void 0;function Jb(a){a=Kb(a);var b=W(a);S(a);return b}
|
||||
function Lb(a,b){function c(g){f[g]||U[g]||(ob[g]?ob[g].forEach(c):(d.push(g),f[g]=!0))}var d=[],f={};b.forEach(c);throw new Ib(a+": "+d.map(Jb).join([", "]));}function Mb(a,b,c){switch(b){case 0:return c?function(d){return e()[d]}:function(d){return v()[d]};case 1:return c?function(d){return x()[d>>1]}:function(d){return ea()[d>>1]};case 2:return c?function(d){return A()[d>>2]}:function(d){return C()[d>>2]};default:throw new TypeError("Unknown integer type: "+a);}}var Nb={};
|
||||
function Ob(){return"object"===typeof globalThis?globalThis:Function("return this")()}function Pb(a,b){var c=U[a];void 0===c&&X(b+" has unknown type "+Jb(a));return c}var Qb={};function Rb(a,b,c){if(0>=a||a>e().length||a&1)return-28;a=Atomics.wait(A(),a>>2,b,c);if("timed-out"===a)return-73;if("not-equal"===a)return-6;if("ok"===a)return 0;throw"Atomics.wait returned an unexpected value "+a;}
|
||||
function Z(a,b){for(var c=arguments.length-2,d=Sb(),f=Tb(8*c),g=f>>3,l=0;l<c;l++)la()[g+l]=arguments[2+l];c=Ub(a,c,f,b);gb(d);return c}var Vb=[],Wb=[],Xb=[0,"undefined"!==typeof document?document:0,"undefined"!==typeof window?window:0];function Yb(a){a=2<a?L(a):a;return Xb[a]||("undefined"!==typeof document?document.querySelector(a):void 0)}
|
||||
function Zb(a,b,c){var d=Yb(a);if(!d)return-4;d.ab&&(A()[d.ab>>2]=b,A()[d.ab+4>>2]=c);if(d.sb||!d.lc)d.sb&&(d=d.sb),a=!1,d.$a&&d.$a.Za&&(a=d.$a.Za.getParameter(2978),a=0===a[0]&&0===a[1]&&a[2]===d.width&&a[3]===d.height),d.width=b,d.height=c,a&&d.$a.Za.viewport(0,0,b,c);else{if(d.ab){d=A()[d.ab+8>>2];a=a?L(a):"";var f=Sb(),g=Tb(12),l=0;if(a){l=Aa(a)+1;var k=R(l);za(a,k,l);l=k}A()[g>>2]=l;A()[g+4>>2]=b;A()[g+8>>2]=c;$b(0,d,657457152,0,l,g);gb(f);return 1}return-4}return 0}
|
||||
function ac(a,b,c){return G?Z(2,1,a,b,c):Zb(a,b,c)}function bc(a){var b=a.getExtension("ANGLE_instanced_arrays");b&&(a.vertexAttribDivisor=function(c,d){b.vertexAttribDivisorANGLE(c,d)},a.drawArraysInstanced=function(c,d,f,g){b.drawArraysInstancedANGLE(c,d,f,g)},a.drawElementsInstanced=function(c,d,f,g,l){b.drawElementsInstancedANGLE(c,d,f,g,l)})}
|
||||
function cc(a){var b=a.getExtension("OES_vertex_array_object");b&&(a.createVertexArray=function(){return b.createVertexArrayOES()},a.deleteVertexArray=function(c){b.deleteVertexArrayOES(c)},a.bindVertexArray=function(c){b.bindVertexArrayOES(c)},a.isVertexArray=function(c){return b.isVertexArrayOES(c)})}function dc(a){var b=a.getExtension("WEBGL_draw_buffers");b&&(a.drawBuffers=function(c,d){b.drawBuffersWEBGL(c,d)})}
|
||||
function ec(a){a||(a=fc);if(!a.Ib){a.Ib=!0;var b=a.Za;bc(b);cc(b);dc(b);b.mc=b.getExtension("EXT_disjoint_timer_query");b.rc=b.getExtension("WEBGL_multi_draw");var c="OES_texture_float OES_texture_half_float OES_standard_derivatives OES_vertex_array_object WEBGL_compressed_texture_s3tc WEBGL_depth_texture OES_element_index_uint EXT_texture_filter_anisotropic EXT_frag_depth WEBGL_draw_buffers ANGLE_instanced_arrays OES_texture_float_linear OES_texture_half_float_linear EXT_blend_minmax EXT_shader_texture_lod EXT_texture_norm16 WEBGL_compressed_texture_pvrtc EXT_color_buffer_half_float WEBGL_color_buffer_float EXT_sRGB WEBGL_compressed_texture_etc1 EXT_disjoint_timer_query WEBGL_compressed_texture_etc WEBGL_compressed_texture_astc EXT_color_buffer_float WEBGL_compressed_texture_s3tc_srgb EXT_disjoint_timer_query_webgl2 WEBKIT_WEBGL_compressed_texture_pvrtc".split(" ");
|
||||
(b.getSupportedExtensions()||[]).forEach(function(d){-1!=c.indexOf(d)&&b.getExtension(d)})}}var fc,gc=["default","low-power","high-performance"],hc={};function ic(){if(!jc){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"===typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:pa||"./this.program"},b;for(b in hc)a[b]=hc[b];var c=[];for(b in a)c.push(b+"="+a[b]);jc=c}return jc}var jc,kc=[null,[],[]];
|
||||
function lc(a){return G?Z(3,1,a):0}function mc(a,b,c,d,f){if(G)return Z(4,1,a,b,c,d,f)}function nc(a,b,c,d){if(G)return Z(5,1,a,b,c,d);for(var f=0,g=0;g<c;g++){for(var l=A()[b+8*g>>2],k=A()[b+(8*g+4)>>2],q=0;q<k;q++){var t=v()[l+q],r=kc[a];0===t||10===t?((1===a?sa:J)(xa(r,0)),r.length=0):r.push(t)}f+=k}A()[d>>2]=f;return 0}
|
||||
function fb(a){if(G)throw"Internal Error! spawnThread() can only ever be called from main application thread!";var b=Q.Cb();if(void 0!==b.La)throw"Internal error!";if(!a.Sa)throw"Internal error, no pthread ptr!";Q.Qa.push(b);for(var c=R(512),d=0;128>d;++d)A()[c+4*d>>2]=0;var f=a.Ra+a.Ta;d=Q.Oa[a.Sa]={worker:b,Ra:a.Ra,Ta:a.Ta,gb:a.gb,wb:a.Sa,threadInfoStruct:a.Sa};var g=d.threadInfoStruct>>2;Atomics.store(C(),g,0);Atomics.store(C(),g+1,0);Atomics.store(C(),g+2,0);Atomics.store(C(),g+17,a.mb);Atomics.store(C(),
|
||||
g+26,c);Atomics.store(C(),g+12,0);Atomics.store(C(),g+10,d.threadInfoStruct);Atomics.store(C(),g+11,42);Atomics.store(C(),g+27,a.Ta);Atomics.store(C(),g+21,a.Ta);Atomics.store(C(),g+20,f);Atomics.store(C(),g+29,f);Atomics.store(C(),g+30,a.mb);Atomics.store(C(),g+32,a.ub);Atomics.store(C(),g+33,a.vb);c=oc()+40;Atomics.store(C(),g+44,c);b.La=d;var l={cmd:"run",start_routine:a.$b,arg:a.Va,threadInfoStruct:a.Sa,selfThreadId:a.Sa,parentThreadId:a.Nb,stackBase:a.Ra,stackSize:a.Ta};b.Xa=function(){l.time=
|
||||
performance.now();b.postMessage(l,a.hc)};b.loaded&&(b.Xa(),delete b.Xa)}function pc(){return P|0}D._pthread_self=pc;
|
||||
function qc(a,b){if(!a)return J("pthread_join attempted on a null thread pointer!"),71;if(G&&selfThreadId==a)return J("PThread "+a+" is attempting to join to itself!"),16;if(!G&&Q.Ka==a)return J("Main thread "+a+" is attempting to join to itself!"),16;if(A()[a+12>>2]!==a)return J("pthread_join attempted on thread "+a+", which does not point to a valid thread, or does not exist anymore!"),71;if(Atomics.load(C(),a+68>>2))return J("Attempted to join thread "+a+", which was already detached!"),28;for(;;){var c=
|
||||
Atomics.load(C(),a+0>>2);if(1==c)return c=Atomics.load(C(),a+4>>2),b&&(A()[b>>2]=c),Atomics.store(C(),a+68>>2,1),G?postMessage({cmd:"cleanupThread",thread:a}):ab(a),0;if(G&&threadInfoStruct&&!Atomics.load(C(),threadInfoStruct+60>>2)&&2==Atomics.load(C(),threadInfoStruct+0>>2))throw"Canceled!";G||eb();Rb(a+0,c,G?100:1)}}function rc(a){return 0===a%4&&(0!==a%100||0===a%400)}function sc(a,b){for(var c=0,d=0;d<=b;c+=a[d++]);return c}
|
||||
var tc=[31,29,31,30,31,30,31,31,30,31,30,31],uc=[31,28,31,30,31,30,31,31,30,31,30,31];function vc(a,b){for(a=new Date(a.getTime());0<b;){var c=a.getMonth(),d=(rc(a.getFullYear())?tc:uc)[c];if(b>d-a.getDate())b-=d-a.getDate()+1,a.setDate(1),11>c?a.setMonth(c+1):(a.setMonth(0),a.setFullYear(a.getFullYear()+1));else{a.setDate(a.getDate()+b);break}}return a}
|
||||
function wc(a,b,c,d){function f(h,p,y){for(h="number"===typeof h?h.toString():h||"";h.length<p;)h=y[0]+h;return h}function g(h,p){return f(h,p,"0")}function l(h,p){function y(I){return 0>I?-1:0<I?1:0}var B;0===(B=y(h.getFullYear()-p.getFullYear()))&&0===(B=y(h.getMonth()-p.getMonth()))&&(B=y(h.getDate()-p.getDate()));return B}function k(h){switch(h.getDay()){case 0:return new Date(h.getFullYear()-1,11,29);case 1:return h;case 2:return new Date(h.getFullYear(),0,3);case 3:return new Date(h.getFullYear(),
|
||||
0,2);case 4:return new Date(h.getFullYear(),0,1);case 5:return new Date(h.getFullYear()-1,11,31);case 6:return new Date(h.getFullYear()-1,11,30)}}function q(h){h=vc(new Date(h.Ja+1900,0,1),h.fb);var p=new Date(h.getFullYear()+1,0,4),y=k(new Date(h.getFullYear(),0,4));p=k(p);return 0>=l(y,h)?0>=l(p,h)?h.getFullYear()+1:h.getFullYear():h.getFullYear()-1}var t=A()[d+40>>2];d={ec:A()[d>>2],dc:A()[d+4>>2],cb:A()[d+8>>2],Ya:A()[d+12>>2],Ua:A()[d+16>>2],Ja:A()[d+20>>2],eb:A()[d+24>>2],fb:A()[d+28>>2],xc:A()[d+
|
||||
32>>2],cc:A()[d+36>>2],fc:t?L(t):""};c=L(c);t={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var r in t)c=c.replace(new RegExp(r,"g"),t[r]);var w="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),
|
||||
z="January February March April May June July August September October November December".split(" ");t={"%a":function(h){return w[h.eb].substring(0,3)},"%A":function(h){return w[h.eb]},"%b":function(h){return z[h.Ua].substring(0,3)},"%B":function(h){return z[h.Ua]},"%C":function(h){return g((h.Ja+1900)/100|0,2)},"%d":function(h){return g(h.Ya,2)},"%e":function(h){return f(h.Ya,2," ")},"%g":function(h){return q(h).toString().substring(2)},"%G":function(h){return q(h)},"%H":function(h){return g(h.cb,
|
||||
2)},"%I":function(h){h=h.cb;0==h?h=12:12<h&&(h-=12);return g(h,2)},"%j":function(h){return g(h.Ya+sc(rc(h.Ja+1900)?tc:uc,h.Ua-1),3)},"%m":function(h){return g(h.Ua+1,2)},"%M":function(h){return g(h.dc,2)},"%n":function(){return"\n"},"%p":function(h){return 0<=h.cb&&12>h.cb?"AM":"PM"},"%S":function(h){return g(h.ec,2)},"%t":function(){return"\t"},"%u":function(h){return h.eb||7},"%U":function(h){var p=new Date(h.Ja+1900,0,1),y=0===p.getDay()?p:vc(p,7-p.getDay());h=new Date(h.Ja+1900,h.Ua,h.Ya);return 0>
|
||||
l(y,h)?g(Math.ceil((31-y.getDate()+(sc(rc(h.getFullYear())?tc:uc,h.getMonth()-1)-31)+h.getDate())/7),2):0===l(y,p)?"01":"00"},"%V":function(h){var p=new Date(h.Ja+1901,0,4),y=k(new Date(h.Ja+1900,0,4));p=k(p);var B=vc(new Date(h.Ja+1900,0,1),h.fb);return 0>l(B,y)?"53":0>=l(p,B)?"01":g(Math.ceil((y.getFullYear()<h.Ja+1900?h.fb+32-y.getDate():h.fb+1-y.getDate())/7),2)},"%w":function(h){return h.eb},"%W":function(h){var p=new Date(h.Ja,0,1),y=1===p.getDay()?p:vc(p,0===p.getDay()?1:7-p.getDay()+1);h=
|
||||
new Date(h.Ja+1900,h.Ua,h.Ya);return 0>l(y,h)?g(Math.ceil((31-y.getDate()+(sc(rc(h.getFullYear())?tc:uc,h.getMonth()-1)-31)+h.getDate())/7),2):0===l(y,p)?"01":"00"},"%y":function(h){return(h.Ja+1900).toString().substring(2)},"%Y":function(h){return h.Ja+1900},"%z":function(h){h=h.cc;var p=0<=h;h=Math.abs(h)/60;return(p?"+":"-")+String("0000"+(h/60*100+h%60)).slice(-4)},"%Z":function(h){return h.fc},"%%":function(){return"%"}};for(r in t)0<=c.indexOf(r)&&(c=c.replace(new RegExp(r,"g"),t[r](d)));r=
|
||||
xc(c);if(r.length>b)return 0;Ha(r,a);return r.length-1}
|
||||
function yc(a){if(G)return Z(6,1,a);switch(a){case 30:return 16384;case 85:return 131072;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:case 79:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;
|
||||
case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1E3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:return"object"===
|
||||
typeof navigator?navigator.hardwareConcurrency||1:1}A()[zc()>>2]=28;return-1}G||Q.Jb();sb=D.InternalError=rb("InternalError");for(var Ac=Array(256),Bc=0;256>Bc;++Bc)Ac[Bc]=String.fromCharCode(Bc);vb=Ac;wb=D.BindingError=rb("BindingError");D.count_emval_handles=function(){for(var a=0,b=5;b<Y.length;++b)void 0!==Y[b]&&++a;return a};D.get_first_emval=function(){for(var a=5;a<Y.length;++a)if(void 0!==Y[a])return Y[a];return null};Ib=D.UnboundTypeError=rb("UnboundTypeError");
|
||||
var Cc=[null,function(a,b){if(G)return Z(1,1,a,b)},ac,lc,mc,nc,yc];function xc(a){var b=Array(Aa(a)+1);ya(a,b,0,b.length);return b}G||Ka.push({Bb:function(){Dc()}});
|
||||
var Gc={i:function(a,b,c,d){K("Assertion failed: "+L(a)+", at: "+[b?L(b):"unknown filename",c,d?L(d):"unknown function"])},K:function(a){return R(a+16)+16},X:function(a,b){return ib(a,b)},I:function(a,b,c){(new jb(a)).Hb(b,c);"uncaught_exception"in kb?kb.xb++:kb.xb=1;throw a;},w:function(a){var b=lb[a];delete lb[a];var c=b.Ob,d=b.Pb,f=b.ob,g=f.map(function(l){return l.Fb}).concat(f.map(function(l){return l.Yb}));tb([a],g,function(l){var k={};f.forEach(function(q,t){var r=l[t],w=q.Db,z=q.Eb,h=l[t+
|
||||
f.length],p=q.Xb,y=q.Zb;k[q.Ab]={read:function(B){return r.fromWireType(w(z,B))},write:function(B,I){var da=[];p(y,B,h.toWireType(da,I));mb(da)}}});return[{name:b.name,fromWireType:function(q){var t={},r;for(r in k)t[r]=k[r].read(q);d(q);return t},toWireType:function(q,t){for(var r in k)if(!(r in t))throw new TypeError('Missing field: "'+r+'"');var w=c();for(r in k)k[r].write(w,t[r]);null!==q&&q.push(d,w);return w},argPackAdvance:8,readValueFromPointer:nb,Pa:d}]})},S:function(a,b,c,d,f){var g=ub(c);
|
||||
b=W(b);V(a,{name:b,fromWireType:function(l){return!!l},toWireType:function(l,k){return k?d:f},argPackAdvance:8,readValueFromPointer:function(l){if(1===c)var k=e();else if(2===c)k=x();else if(4===c)k=A();else throw new TypeError("Unknown boolean type size: "+b);return this.fromWireType(k[l>>g])},Pa:null})},R:function(a,b){b=W(b);V(a,{name:b,fromWireType:function(c){var d=Y[c].value;yb(c);return d},toWireType:function(c,d){return zb(d)},argPackAdvance:8,readValueFromPointer:nb,Pa:null})},t:function(a,
|
||||
b,c){c=ub(c);b=W(b);V(a,{name:b,fromWireType:function(d){return d},toWireType:function(d,f){if("number"!==typeof f&&"boolean"!==typeof f)throw new TypeError('Cannot convert "'+Ab(f)+'" to '+this.name);return f},argPackAdvance:8,readValueFromPointer:Bb(b,c),Pa:null})},v:function(a,b,c,d,f,g){var l=Fb(b,c);a=W(a);f=Hb(d,f);Eb(a,function(){Lb("Cannot call "+a+" due to unbound types",l)},b-1);tb([],l,function(k){var q=a,t=a;k=[k[0],null].concat(k.slice(1));var r=f,w=k.length;2>w&&X("argTypes array size mismatch! Must at least get return value and 'this' types!");
|
||||
for(var z=null!==k[1]&&!1,h=!1,p=1;p<k.length;++p)if(null!==k[p]&&void 0===k[p].Pa){h=!0;break}var y="void"!==k[0].name,B="",I="";for(p=0;p<w-2;++p)B+=(0!==p?", ":"")+"arg"+p,I+=(0!==p?", ":"")+"arg"+p+"Wired";t="return function "+pb(t)+"("+B+") {\nif (arguments.length !== "+(w-2)+") {\nthrowBindingError('function "+t+" called with ' + arguments.length + ' arguments, expected "+(w-2)+" args!');\n}\n";h&&(t+="var destructors = [];\n");var da=h?"destructors":"null";B="throwBindingError invoker fn runDestructors retType classParam".split(" ");
|
||||
r=[X,r,g,mb,k[0],k[1]];z&&(t+="var thisWired = classParam.toWireType("+da+", this);\n");for(p=0;p<w-2;++p)t+="var arg"+p+"Wired = argType"+p+".toWireType("+da+", arg"+p+"); // "+k[p+2].name+"\n",B.push("argType"+p),r.push(k[p+2]);z&&(I="thisWired"+(0<I.length?", ":"")+I);t+=(y?"var rv = ":"")+"invoker(fn"+(0<I.length?", ":"")+I+");\n";if(h)t+="runDestructors(destructors);\n";else for(p=z?1:2;p<k.length;++p)w=1===p?"thisWired":"arg"+(p-2)+"Wired",null!==k[p].Pa&&(t+=w+"_dtor("+w+"); // "+k[p].name+
|
||||
"\n",B.push(w+"_dtor"),r.push(k[p].Pa));y&&(t+="var ret = retType.fromWireType(rv);\nreturn ret;\n");B.push(t+"}\n");k=Cb(B).apply(null,r);p=b-1;if(!D.hasOwnProperty(q))throw new sb("Replacing nonexistant public symbol");void 0!==D[q].Na&&void 0!==p?D[q].Na[p]=k:(D[q]=k,D[q].yb=p);return[]})},j:function(a,b,c,d,f){function g(t){return t}b=W(b);-1===f&&(f=4294967295);var l=ub(c);if(0===d){var k=32-8*c;g=function(t){return t<<k>>>k}}var q=-1!=b.indexOf("unsigned");V(a,{name:b,fromWireType:g,toWireType:function(t,
|
||||
r){if("number"!==typeof r&&"boolean"!==typeof r)throw new TypeError('Cannot convert "'+Ab(r)+'" to '+this.name);if(r<d||r>f)throw new TypeError('Passing a number "'+Ab(r)+'" from JS side to C/C++ side to an argument of type "'+b+'", which is outside the valid range ['+d+", "+f+"]!");return q?r>>>0:r|0},argPackAdvance:8,readValueFromPointer:Mb(b,l,0!==d),Pa:null})},h:function(a,b,c){function d(g){g>>=2;var l=C();return new f(n,l[g+1],l[g])}var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,
|
||||
Uint32Array,Float32Array,Float64Array][b];c=W(c);V(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{Gb:!0})},u:function(a,b){b=W(b);var c="std::string"===b;V(a,{name:b,fromWireType:function(d){var f=C()[d>>2];if(c)for(var g=d+4,l=0;l<=f;++l){var k=d+4+l;if(l==f||0==v()[k]){g=L(g,k-g);if(void 0===q)var q=g;else q+=String.fromCharCode(0),q+=g;g=k+1}}else{q=Array(f);for(l=0;l<f;++l)q[l]=String.fromCharCode(v()[d+4+l]);q=q.join("")}S(d);return q},toWireType:function(d,f){f instanceof
|
||||
ArrayBuffer&&(f=new Uint8Array(f));var g="string"===typeof f;g||f instanceof Uint8Array||f instanceof Uint8ClampedArray||f instanceof Int8Array||X("Cannot pass non-string to std::string");var l=(c&&g?function(){return Aa(f)}:function(){return f.length})(),k=R(4+l+1);C()[k>>2]=l;if(c&&g)za(f,k+4,l+1);else if(g)for(g=0;g<l;++g){var q=f.charCodeAt(g);255<q&&(S(k),X("String has UTF-16 code units that do not fit in 8 bits"));v()[k+4+g]=q}else for(g=0;g<l;++g)v()[k+4+g]=f[g];null!==d&&d.push(S,k);return k},
|
||||
argPackAdvance:8,readValueFromPointer:nb,Pa:function(d){S(d)}})},q:function(a,b,c){c=W(c);if(2===b){var d=Ba;var f=Ca;var g=Da;var l=function(){return ea()};var k=1}else 4===b&&(d=Ea,f=Fa,g=Ga,l=function(){return C()},k=2);V(a,{name:c,fromWireType:function(q){for(var t=C()[q>>2],r=l(),w,z=q+4,h=0;h<=t;++h){var p=q+4+h*b;if(h==t||0==r[p>>k])z=d(z,p-z),void 0===w?w=z:(w+=String.fromCharCode(0),w+=z),z=p+b}S(q);return w},toWireType:function(q,t){"string"!==typeof t&&X("Cannot pass non-string to C++ string type "+
|
||||
c);var r=g(t),w=R(4+r+b);C()[w>>2]=r>>k;f(t,w+4,r+b);null!==q&&q.push(S,w);return w},argPackAdvance:8,readValueFromPointer:nb,Pa:function(q){S(q)}})},x:function(a,b,c,d,f,g){lb[a]={name:W(b),Ob:Hb(c,d),Pb:Hb(f,g),ob:[]}},l:function(a,b,c,d,f,g,l,k,q,t){lb[a].ob.push({Ab:W(b),Fb:c,Db:Hb(d,f),Eb:g,Yb:l,Xb:Hb(k,q),Zb:t})},T:function(a,b){b=W(b);V(a,{oc:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},H:function(a,b){if(a==b)postMessage({cmd:"processQueuedMainThreadWork"});
|
||||
else if(G)postMessage({targetThread:a,cmd:"processThreadQueue"});else{a=(a=Q.Oa[a])&&a.worker;if(!a)return;a.postMessage({cmd:"processThreadQueue"})}return 1},m:yb,W:function(a){if(0===a)return zb(Ob());var b=Nb[a];a=void 0===b?W(a):b;return zb(Ob()[a])},V:function(a){4<a&&(Y[a].jb+=1)},y:function(a,b,c,d){a||X("Cannot use deleted val. handle = "+a);a=Y[a].value;var f=Qb[b];if(!f){f="";for(var g=0;g<b;++g)f+=(0!==g?", ":"")+"arg"+g;var l="return function emval_allocator_"+b+"(constructor, argTypes, args) {\n";
|
||||
for(g=0;g<b;++g)l+="var argType"+g+" = requireRegisteredType(Module['HEAP32'][(argTypes >>> 2) + "+g+'], "parameter '+g+'");\nvar arg'+g+" = argType"+g+".readValueFromPointer(args);\nargs += argType"+g+"['argPackAdvance'];\n";f=(new Function("requireRegisteredType","Module","__emval_register",l+("var obj = new constructor("+f+");\nreturn __emval_register(obj);\n}\n")))(Pb,D,zb);Qb[b]=f}return f(a,c,d)},b:function(){K()},n:function(a,b,c){Wb.length=0;var d;for(c>>=2;d=v()[b++];)(d=105>d)&&c&1&&c++,
|
||||
Wb.push(d?la()[c++>>1]:A()[c]),++c;return Ua[a].apply(null,Wb)},J:function(){},r:function(){},f:Rb,g:$a,d:hb,p:function(){return Ya|0},o:function(){return Xa|0},C:function(a,b,c){v().copyWithin(a,b,b+c)},E:function(a,b,c){Vb.length=b;c>>=3;for(var d=0;d<b;d++)Vb[d]=la()[c+d];return(0>a?Ua[-a-1]:Cc[a]).apply(null,Vb)},k:function(a){a>>>=0;var b=v().length;if(a<=b||2147483648<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);d=Math.max(16777216,a,d);0<d%65536&&(d+=65536-d%
|
||||
65536);a:{try{m.grow(Math.min(2147483648,d)-n.byteLength+65535>>>16);u(m.buffer);var f=1;break a}catch(g){}f=void 0}if(f)return!0}return!1},F:function(a,b,c){return Yb(a)?Zb(a,b,c):ac(a,b,c)},e:function(){},G:function(a,b){var c={};b>>=2;c.alpha=!!A()[b];c.depth=!!A()[b+1];c.stencil=!!A()[b+2];c.antialias=!!A()[b+3];c.premultipliedAlpha=!!A()[b+4];c.preserveDrawingBuffer=!!A()[b+5];var d=A()[b+6];c.powerPreference=gc[d];c.failIfMajorPerformanceCaveat=!!A()[b+7];c.Mb=A()[b+8];c.qc=A()[b+9];c.nb=A()[b+
|
||||
10];c.zb=A()[b+11];c.tc=A()[b+12];c.uc=A()[b+13];a=Yb(a);!a||c.zb?c=0:(a=a.getContext("webgl",c))?(b=R(8),A()[b+4>>2]=P|0,d={nc:b,attributes:c,version:c.Mb,Za:a},a.canvas&&(a.canvas.$a=d),("undefined"===typeof c.nb||c.nb)&&ec(d),c=b):c=0;return c},O:function(a,b){var c=0;ic().forEach(function(d,f){var g=b+c;f=A()[a+4*f>>2]=g;for(g=0;g<d.length;++g)e()[f++>>0]=d.charCodeAt(g);e()[f>>0]=0;c+=d.length+1});return 0},P:function(a,b){var c=ic();A()[a>>2]=c.length;var d=0;c.forEach(function(f){d+=f.length+
|
||||
1});A()[b>>2]=d;return 0},Q:lc,z:mc,s:nc,B:function(){Q.Kb()},a:m||D.wasmMemory,D:ib,U:function(a,b,c,d){if("undefined"===typeof SharedArrayBuffer)return J("Current environment does not support SharedArrayBuffer, pthreads are not available!"),6;if(!a)return J("pthread_create called with a null thread pointer!"),28;var f=[];if(G&&0===f.length)return Ec(687865856,a,b,c,d);var g=0,l=0,k=0,q=0;if(b){var t=A()[b>>2];t+=81920;g=A()[b+8>>2];l=0!==A()[b+12>>2];if(0===A()[b+16>>2]){var r=A()[b+20>>2],w=A()[b+
|
||||
24>>2];k=b+20;q=b+24;var z=Q.hb?Q.hb:P|0;if(k||q)if(z)if(A()[z+12>>2]!==z)J("pthread_getschedparam attempted on thread "+z+", which does not point to a valid thread, or does not exist anymore!");else{var h=Atomics.load(C(),z+108+20>>2);z=Atomics.load(C(),z+108+24>>2);k&&(A()[k>>2]=h);q&&(A()[q>>2]=z)}else J("pthread_getschedparam called with a null thread pointer!");k=A()[b+20>>2];q=A()[b+24>>2];A()[b+20>>2]=r;A()[b+24>>2]=w}else k=A()[b+20>>2],q=A()[b+24>>2]}else t=2097152;(b=0==g)?g=Fc(16,t):(g-=
|
||||
t,wa(0<g));r=R(232);for(w=0;58>w;++w)C()[(r>>2)+w]=0;A()[a>>2]=r;A()[r+12>>2]=r;a=r+156;A()[a>>2]=a;c={Ra:g,Ta:t,gb:b,ub:k,vb:q,mb:l,$b:c,Sa:r,Nb:P|0,Va:d,hc:f};G?(c.kc="spawnThread",postMessage(c,f)):fb(c);return 0},L:function(a,b){return qc(a,b)},c:pc,A:function(){},N:function(a,b,c,d){return wc(a,b,c,d)},M:yc};
|
||||
(function(){function a(f,g){D.asm=f.exports;M=D.asm.Y;ua=g;if(!G){var l=Q.Ma.length;Q.Ma.forEach(function(k){Q.qb(k,function(){if(!--l&&(N--,D.monitorRunDependencies&&D.monitorRunDependencies(N),0==N&&(null!==Oa&&(clearInterval(Oa),Oa=null),Pa))){var q=Pa;Pa=null;q()}})})}}function b(f){a(f.instance,f.module)}function c(f){return Sa().then(function(g){return WebAssembly.instantiate(g,d)}).then(f,function(g){J("failed to asynchronously prepare wasm: "+g);K(g)})}var d={a:Gc};G||(wa(!G,"addRunDependency cannot be used in a pthread worker"),
|
||||
N++,D.monitorRunDependencies&&D.monitorRunDependencies(N));if(D.instantiateWasm)try{return D.instantiateWasm(d,a)}catch(f){return J("Module.instantiateWasm callback failed with error: "+f),!1}(function(){return ta||"function"!==typeof WebAssembly.instantiateStreaming||Qa()||"function"!==typeof fetch?c(b):fetch(O,{credentials:"same-origin"}).then(function(f){return WebAssembly.instantiateStreaming(f,d).then(b,function(g){J("wasm streaming compile failed: "+g);J("falling back to ArrayBuffer instantiation");
|
||||
return c(b)})})})().catch(oa);return{}})();var Dc=D.___wasm_call_ctors=function(){return(Dc=D.___wasm_call_ctors=D.asm.Z).apply(null,arguments)},R=D._malloc=function(){return(R=D._malloc=D.asm._).apply(null,arguments)},S=D._free=function(){return(S=D._free=D.asm.$).apply(null,arguments)},zc=D.___errno_location=function(){return(zc=D.___errno_location=D.asm.aa).apply(null,arguments)},Kb=D.___getTypeName=function(){return(Kb=D.___getTypeName=D.asm.ba).apply(null,arguments)};
|
||||
D.___embind_register_native_and_builtin_types=function(){return(D.___embind_register_native_and_builtin_types=D.asm.ca).apply(null,arguments)};D.___em_js__initPthreadsJS=function(){return(D.___em_js__initPthreadsJS=D.asm.da).apply(null,arguments)};
|
||||
var oc=D._emscripten_get_global_libc=function(){return(oc=D._emscripten_get_global_libc=D.asm.ea).apply(null,arguments)},Sb=D.stackSave=function(){return(Sb=D.stackSave=D.asm.fa).apply(null,arguments)},gb=D.stackRestore=function(){return(gb=D.stackRestore=D.asm.ga).apply(null,arguments)},Tb=D.stackAlloc=function(){return(Tb=D.stackAlloc=D.asm.ha).apply(null,arguments)},Fc=D._memalign=function(){return(Fc=D._memalign=D.asm.ia).apply(null,arguments)};
|
||||
D._emscripten_main_browser_thread_id=function(){return(D._emscripten_main_browser_thread_id=D.asm.ja).apply(null,arguments)};var db=D.___pthread_tsd_run_dtors=function(){return(db=D.___pthread_tsd_run_dtors=D.asm.ka).apply(null,arguments)},eb=D._emscripten_main_thread_process_queued_calls=function(){return(eb=D._emscripten_main_thread_process_queued_calls=D.asm.la).apply(null,arguments)};
|
||||
D._emscripten_current_thread_process_queued_calls=function(){return(D._emscripten_current_thread_process_queued_calls=D.asm.ma).apply(null,arguments)};var bb=D._emscripten_register_main_browser_thread_id=function(){return(bb=D._emscripten_register_main_browser_thread_id=D.asm.na).apply(null,arguments)},Ta=D._do_emscripten_dispatch_to_thread=function(){return(Ta=D._do_emscripten_dispatch_to_thread=D.asm.oa).apply(null,arguments)};
|
||||
D._emscripten_async_run_in_main_thread=function(){return(D._emscripten_async_run_in_main_thread=D.asm.pa).apply(null,arguments)};D._emscripten_sync_run_in_main_thread=function(){return(D._emscripten_sync_run_in_main_thread=D.asm.qa).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_0=function(){return(D._emscripten_sync_run_in_main_thread_0=D.asm.ra).apply(null,arguments)};
|
||||
D._emscripten_sync_run_in_main_thread_1=function(){return(D._emscripten_sync_run_in_main_thread_1=D.asm.sa).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_2=function(){return(D._emscripten_sync_run_in_main_thread_2=D.asm.ta).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_xprintf_varargs=function(){return(D._emscripten_sync_run_in_main_thread_xprintf_varargs=D.asm.ua).apply(null,arguments)};
|
||||
D._emscripten_sync_run_in_main_thread_3=function(){return(D._emscripten_sync_run_in_main_thread_3=D.asm.va).apply(null,arguments)};var Ec=D._emscripten_sync_run_in_main_thread_4=function(){return(Ec=D._emscripten_sync_run_in_main_thread_4=D.asm.wa).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_5=function(){return(D._emscripten_sync_run_in_main_thread_5=D.asm.xa).apply(null,arguments)};
|
||||
D._emscripten_sync_run_in_main_thread_6=function(){return(D._emscripten_sync_run_in_main_thread_6=D.asm.ya).apply(null,arguments)};D._emscripten_sync_run_in_main_thread_7=function(){return(D._emscripten_sync_run_in_main_thread_7=D.asm.za).apply(null,arguments)};
|
||||
var Ub=D._emscripten_run_in_main_runtime_thread_js=function(){return(Ub=D._emscripten_run_in_main_runtime_thread_js=D.asm.Aa).apply(null,arguments)},$b=D.__emscripten_call_on_thread=function(){return($b=D.__emscripten_call_on_thread=D.asm.Ba).apply(null,arguments)};D._emscripten_tls_init=function(){return(D._emscripten_tls_init=D.asm.Ca).apply(null,arguments)};D.dynCall_viijii=function(){return(D.dynCall_viijii=D.asm.Da).apply(null,arguments)};
|
||||
D.dynCall_iiji=function(){return(D.dynCall_iiji=D.asm.Ea).apply(null,arguments)};D.dynCall_jiji=function(){return(D.dynCall_jiji=D.asm.Fa).apply(null,arguments)};D.dynCall_iiiiiijj=function(){return(D.dynCall_iiiiiijj=D.asm.Ga).apply(null,arguments)};D.dynCall_iiiiij=function(){return(D.dynCall_iiiiij=D.asm.Ha).apply(null,arguments)};D.dynCall_iiiiijj=function(){return(D.dynCall_iiiiijj=D.asm.Ia).apply(null,arguments)};var cb=D._main_thread_futex=3067960;D.PThread=Q;D.PThread=Q;D._pthread_self=pc;
|
||||
D.wasmMemory=m;D.ExitStatus=Hc;var Ic;function Hc(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}Pa=function Jc(){Ic||Kc();Ic||(Pa=Jc)};
|
||||
function Kc(){function a(){if(!Ic&&(Ic=!0,D.calledRun=!0,!va)){Va(Ka);G||Va(La);na(D);if(D.onRuntimeInitialized)D.onRuntimeInitialized();if(!G){if(D.postRun)for("function"==typeof D.postRun&&(D.postRun=[D.postRun]);D.postRun.length;){var b=D.postRun.shift();Ma.unshift(b)}Va(Ma)}}}if(!(0<N)){if(!G){if(D.preRun)for("function"==typeof D.preRun&&(D.preRun=[D.preRun]);D.preRun.length;)Na();Va(Ja)}0<N||(D.setStatus?(D.setStatus("Running..."),setTimeout(function(){setTimeout(function(){D.setStatus("")},
|
||||
1);a()},1)):a())}}D.run=Kc;if(D.preInit)for("function"==typeof D.preInit&&(D.preInit=[D.preInit]);0<D.preInit.length;)D.preInit.pop()();G||(noExitRuntime=!0);G?Q.Lb():Kc();
|
||||
|
||||
|
||||
return jxl_enc_mt_simd.ready
|
||||
}
|
||||
);
|
||||
})();
|
||||
export default jxl_enc_mt_simd;
|
||||
1
codecs/jxl/enc/jxl_enc_mt_simd.worker.js
generated
@@ -1 +0,0 @@
|
||||
var threadInfoStruct=0;var selfThreadId=0;var parentThreadId=0;var initializedJS=false;var Module={};function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:selfThreadId})}var err=threadPrintErr;this.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);Module["wasmModule"]=null;receiveInstance(instance);return instance.exports};this.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;import(e.data.urlOrBlob).then(function(jxl_enc_mt_simd){return jxl_enc_mt_simd.default(Module)}).then(function(instance){Module=instance;postMessage({"cmd":"loaded"})})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;threadInfoStruct=e.data.threadInfoStruct;Module["registerPthreadPtr"](threadInfoStruct,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/0);selfThreadId=e.data.selfThreadId;parentThreadId=e.data.parentThreadId;var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["_emscripten_tls_init"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].setThreadStatus(Module["_pthread_self"](),1);if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["dynCall"]("ii",e.data.start_routine,[e.data.arg]);if(!Module["getNoExitRuntime"]())Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){Atomics.store(Module["HEAPU32"],(threadInfoStruct+4)>>/*C_STRUCTS.pthread.threadExitCode*/2,(ex instanceof Module["ExitStatus"])?ex.status:-2);/*A custom entry specific to Emscripten denoting that the thread crashed.*/Atomics.store(Module["HEAPU32"],(threadInfoStruct+0)>>/*C_STRUCTS.pthread.threadStatus*/2,1);Module["_emscripten_futex_wake"](threadInfoStruct+0,/*C_STRUCTS.pthread.threadStatus*/2147483647);if(!(ex instanceof Module["ExitStatus"]))throw ex}}}else if(e.data.cmd==="cancel"){if(threadInfoStruct){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(threadInfoStruct){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}};
|
||||
@@ -41,7 +41,6 @@ const assetRe = new RegExp('/fake/path/to/asset/([^/]+)/', 'g');
|
||||
const appendCssModule = '\0appendCss';
|
||||
const appendCssSource = `
|
||||
export default function appendCss(css) {
|
||||
if (__PRERENDER__) return;
|
||||
const style = document.createElement('style');
|
||||
style.textContent = css;
|
||||
document.head.append(style);
|
||||
@@ -90,7 +89,7 @@ export default function (resolveFileUrl) {
|
||||
}),
|
||||
postCSSUrl({
|
||||
url: ({ relativePath, url }) => {
|
||||
if (/^((https?|data):|#)/.test(url)) return url;
|
||||
if (/^(https?|data):/.test(url)) return url;
|
||||
const parsedPath = parsePath(relativePath);
|
||||
const source = readFileSync(
|
||||
resolvePath(dirname(path), relativePath),
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function initialCssPlugin() {
|
||||
async load(id) {
|
||||
if (id !== initialCssModule) return;
|
||||
|
||||
const matches = await globP('shared/prerendered-app/**/*.css', {
|
||||
const matches = await globP('shared/initial-app/**/*.css', {
|
||||
nodir: true,
|
||||
cwd: path.join(process.cwd(), 'src'),
|
||||
});
|
||||
|
||||
12
package-lock.json
generated
@@ -182,12 +182,6 @@
|
||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/dedent": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/dedent/-/dedent-0.7.0.tgz",
|
||||
"integrity": "sha512-EGlKlgMhnLt/cM4DbUSafFdrkeJoC9Mvnj0PUCU7tFmTjMjNRT957kXCx0wYm3JuEq4o4ZsS5vG+NlkM2DMd2A==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/estree": {
|
||||
"version": "0.0.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
||||
@@ -6036,9 +6030,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.5.7",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.7.tgz",
|
||||
"integrity": "sha512-4oEpz75t/0UNcwmcsjk+BIcDdk68oao+7kxcpc1hQPNs2Oo3ZL9xFz8UBf350mxk/VEdD41L5b4l2dE3Ug3RYg==",
|
||||
"version": "10.5.5",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.5.tgz",
|
||||
"integrity": "sha512-5ONLNH1SXMzzbQoExZX4TELemNt+TEDb622xXFNfZngjjM9qtrzseJt+EfiUu4TZ6EJ95X5sE1ES4yqHFSIdhg==",
|
||||
"dev": true
|
||||
},
|
||||
"preact-render-to-string": {
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||
"@rollup/plugin-replace": "^2.3.4",
|
||||
"@surma/rollup-plugin-off-main-thread": "^1.4.2",
|
||||
"@types/dedent": "^0.7.0",
|
||||
"@types/node": "^14.14.7",
|
||||
"comlink": "^4.3.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"dedent": "^0.7.0",
|
||||
"del": "^5.1.0",
|
||||
"file-drop-element": "^1.0.1",
|
||||
"husky": "^4.3.0",
|
||||
@@ -35,7 +33,7 @@
|
||||
"postcss-nested": "^4.2.3",
|
||||
"postcss-simple-vars": "^5.0.2",
|
||||
"postcss-url": "^8.0.0",
|
||||
"preact": "^10.5.7",
|
||||
"preact": "^10.5.5",
|
||||
"preact-render-to-string": "^5.1.11",
|
||||
"prettier": "^2.1.2",
|
||||
"pretty-bytes": "^5.4.1",
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type { FileDropEvent } from 'file-drop-element';
|
||||
import type SnackBarElement from 'shared/custom-els/snack-bar';
|
||||
import type { SnackOptions } from 'shared/custom-els/snack-bar';
|
||||
import type SnackBarElement from 'shared/initial-app/custom-els/snack-bar';
|
||||
import type { SnackOptions } from 'shared/initial-app/custom-els/snack-bar';
|
||||
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { linkRef } from 'shared/prerendered-app/util';
|
||||
import { linkRef } from 'shared/initial-app/util';
|
||||
import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
import 'file-drop-element';
|
||||
import 'shared/custom-els/snack-bar';
|
||||
import Intro from 'shared/prerendered-app/Intro';
|
||||
import 'shared/custom-els/loading-spinner';
|
||||
import 'shared/initial-app/custom-els/snack-bar';
|
||||
import Intro from 'shared/initial-app/Intro';
|
||||
import 'shared/initial-app/custom-els/loading-spinner';
|
||||
|
||||
const ROUTE_EDITOR = '/editor';
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
border: 2px dashed #fff;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--pink);
|
||||
background-color: rgba(88, 116, 88, 0.2);
|
||||
border-color: rgba(65, 129, 65, 0.5);
|
||||
border-radius: 10px;
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/// <reference path="../../../shared/custom-els/snack-bar/missing-types.d.ts" />
|
||||
/// <reference path="../../../shared/custom-els/loading-spinner/missing-types.d.ts" />
|
||||
/// <reference path="../../../shared/initial-app/custom-els/snack-bar/missing-types.d.ts" />
|
||||
/// <reference path="../../../shared/initial-app/custom-els/loading-spinner/missing-types.d.ts" />
|
||||
import type { FileDropElement, FileDropEvent } from 'file-drop-element';
|
||||
|
||||
interface FileDropAttributes extends preact.JSX.HTMLAttributes {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { h, Component, createRef } from 'preact';
|
||||
import { drawDataToCanvas } from '../util';
|
||||
|
||||
export interface CanvasImageProps
|
||||
extends h.JSX.HTMLAttributes<HTMLCanvasElement> {
|
||||
image?: ImageData;
|
||||
}
|
||||
|
||||
export default class CanvasImage extends Component<CanvasImageProps> {
|
||||
canvas = createRef<HTMLCanvasElement>();
|
||||
componentDidUpdate(prevProps: CanvasImageProps) {
|
||||
if (this.props.image !== prevProps.image) {
|
||||
this.draw(this.props.image);
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
if (this.props.image) {
|
||||
this.draw(this.props.image);
|
||||
}
|
||||
}
|
||||
draw(image?: ImageData) {
|
||||
const canvas = this.canvas.current;
|
||||
if (!canvas) return;
|
||||
if (!image) canvas.getContext('2d');
|
||||
else drawDataToCanvas(canvas, image);
|
||||
}
|
||||
render({ image, ...props }: CanvasImageProps) {
|
||||
return (
|
||||
<canvas
|
||||
ref={this.canvas}
|
||||
width={image?.width}
|
||||
height={image?.height}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import {
|
||||
Component,
|
||||
cloneElement,
|
||||
createRef,
|
||||
toChildArray,
|
||||
ComponentChildren,
|
||||
RefObject,
|
||||
} from 'preact';
|
||||
|
||||
interface Props {
|
||||
children: ComponentChildren;
|
||||
onClick?(e: MouseEvent | KeyboardEvent): void;
|
||||
}
|
||||
|
||||
export class ClickOutsideDetector extends Component<Props> {
|
||||
private _roots: RefObject<Element>[] = [];
|
||||
|
||||
private handleClick = (e: MouseEvent) => {
|
||||
let target = e.target as Node;
|
||||
// check if the click came from within any of our child elements:
|
||||
for (const { current: root } of this._roots) {
|
||||
if (root && (root === target || root.contains(target))) return;
|
||||
}
|
||||
const { onClick } = this.props;
|
||||
if (onClick) onClick(e);
|
||||
};
|
||||
|
||||
private handleKey = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
const { onClick } = this.props;
|
||||
if (onClick) onClick(e);
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
addEventListener('click', this.handleClick, { passive: true });
|
||||
addEventListener('keydown', this.handleKey, { passive: true });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
removeEventListener('click', this.handleClick);
|
||||
removeEventListener('keydown', this.handleKey);
|
||||
}
|
||||
|
||||
render({ children }: Props) {
|
||||
this._roots = [];
|
||||
return toChildArray(children).map((child) => {
|
||||
if (typeof child !== 'object') return child;
|
||||
const ref = createRef();
|
||||
this._roots.push(ref);
|
||||
return cloneElement(child, { ref });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import { h, cloneElement, Component, VNode, createRef, ComponentChildren, ComponentProps } from "preact";
|
||||
import { ClickOutsideDetector } from "../ClickOutsideDetector";
|
||||
import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
|
||||
type Anchor = 'left' | 'right' | 'top' | 'bottom';
|
||||
|
||||
interface Props extends ComponentProps<'aside'> {
|
||||
showing?: boolean;
|
||||
direction?: 'up' | 'down';
|
||||
anchor?: Anchor | Anchor[];
|
||||
toggle?: VNode;
|
||||
children?: ComponentChildren;
|
||||
}
|
||||
|
||||
interface State {
|
||||
showing: boolean;
|
||||
}
|
||||
|
||||
export default class Flyout extends Component<Props, State> {
|
||||
state = {
|
||||
showing: this.props.showing === true
|
||||
};
|
||||
|
||||
private menu = createRef<HTMLElement>();
|
||||
|
||||
private hide = () => {
|
||||
this.setState({ showing: false });
|
||||
};
|
||||
|
||||
private toggle = () => {
|
||||
this.setState({ showing: !this.state.showing });
|
||||
};
|
||||
|
||||
componentWillReceiveProps({ showing }: Props) {
|
||||
if (showing !== this.props.showing) {
|
||||
this.setState({ showing });
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
if (this.state.showing && !prevState.showing) {
|
||||
const menu = this.menu.current;
|
||||
if (menu) {
|
||||
let toFocus = menu.firstElementChild;
|
||||
for (let child of menu.children) {
|
||||
if (child.hasAttribute('autofocus')) {
|
||||
toFocus = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// @ts-ignore-next
|
||||
if (toFocus) toFocus.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render({ direction, anchor, toggle, children, ...props }: Props, { showing }: State) {
|
||||
const toggleProps = {
|
||||
flyoutOpen: showing,
|
||||
onClick: this.toggle
|
||||
};
|
||||
|
||||
const anchorText = Array.isArray(anchor) ? anchor.join(' ') : anchor;
|
||||
|
||||
return (
|
||||
<span class={style.wrap} data-flyout-open={showing ? '' : undefined}>
|
||||
<ClickOutsideDetector onClick={this.hide}>
|
||||
{toggle && cloneElement(toggle, toggleProps)}
|
||||
|
||||
<aside
|
||||
{...props}
|
||||
ref={this.menu}
|
||||
hidden={!showing}
|
||||
data-anchor={anchorText}
|
||||
data-direction={direction}
|
||||
>
|
||||
{children}
|
||||
</aside>
|
||||
</ClickOutsideDetector>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
.wrap {
|
||||
display: inline;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wrap > aside:last-of-type {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 100%;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
overflow: visible;
|
||||
outline: none;
|
||||
will-change: transform, opacity;
|
||||
animation: menuOpen 350ms ease forwards 1;
|
||||
--flyout-offset-y: -20px;
|
||||
|
||||
&[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* align to the right edge */
|
||||
&[data-anchor*='right'] {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
/* open to the left */
|
||||
&[data-direction*='left'] {
|
||||
right: 0;
|
||||
left: auto;
|
||||
&[anchor*='right'] {
|
||||
right: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* align to the top edge */
|
||||
&[data-anchor*='top'] {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* open to the left */
|
||||
&[data-direction*='up'] {
|
||||
bottom: 100%;
|
||||
top: auto;
|
||||
flex-direction: column-reverse;
|
||||
--flyout-offset-y: 20px;
|
||||
&[data-anchor*='bottom'] {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@media (min-width: 860px) {
|
||||
flex-direction: column-reverse;
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@keyframes menuOpen {
|
||||
0% {
|
||||
transform: translateY(var(--flyout-offset-y, 0));
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -18,5 +18,5 @@
|
||||
}
|
||||
|
||||
.checked {
|
||||
fill: var(--main-theme-color);
|
||||
fill: #34b9eb;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ function getPrescision(value: string): number {
|
||||
|
||||
class RangeInputElement extends HTMLElement {
|
||||
private _input: HTMLInputElement;
|
||||
private _valueDisplay?: HTMLSpanElement;
|
||||
private _valueDisplay?: HTMLDivElement;
|
||||
private _ignoreChange = false;
|
||||
|
||||
static get observedAttributes() {
|
||||
@@ -66,13 +66,13 @@ class RangeInputElement extends HTMLElement {
|
||||
this.innerHTML =
|
||||
`<div class="${style.thumbWrapper}">` +
|
||||
`<div class="${style.thumb}"></div>` +
|
||||
`<div class="${style.valueDisplay}"><svg width="32" height="62"><path d="M27.3 27.3C25 29.6 17 35.8 17 43v3c0 3 2.5 5 3.2 5.8a6 6 0 1 1-8.5 0C12.6 51 15 49 15 46v-3c0-7.2-8-13.4-10.3-15.7A16 16 0 0 1 16 0a16 16 0 0 1 11.3 27.3z"/></svg><span></span></div>` +
|
||||
`<div class="${style.valueDisplay}"></div>` +
|
||||
'</div>';
|
||||
|
||||
this.insertBefore(this._input, this.firstChild);
|
||||
this._valueDisplay = this.querySelector(
|
||||
'.' + style.valueDisplay + ' > span',
|
||||
) as HTMLSpanElement;
|
||||
'.' + style.valueDisplay,
|
||||
) as HTMLDivElement;
|
||||
// Set inline styles (this is useful when used with frameworks which might clear inline styles)
|
||||
this._update();
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@ range-input::before {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
border-radius: 1px;
|
||||
background: linear-gradient(var(--main-theme-color), var(--main-theme-color))
|
||||
0 / var(--value-percent, 0%) 100% no-repeat var(--medium-light-gray);
|
||||
box-shadow: 0 -0.5px 0 rgba(0, 0, 0, 0.3),
|
||||
inset 0 0.5px 0 rgba(255, 255, 255, 0.2), 0 0.5px 0 rgba(255, 255, 255, 0.3);
|
||||
background: linear-gradient(#34b9eb, #218ab1) 0 / var(--value-percent, 0%)
|
||||
100% no-repeat #eee;
|
||||
}
|
||||
|
||||
.input {
|
||||
@@ -39,12 +41,14 @@ range-input::before {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
left: 0;
|
||||
left: var(--value-percent, 0%);
|
||||
margin-left: -6px;
|
||||
background: var(--main-theme-color);
|
||||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><circle cx="5" cy="5" r="1" fill="%235D509E" /></svg>')
|
||||
center no-repeat #34b9eb;
|
||||
border-radius: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-shadow: 0 0.5px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.thumb-wrapper {
|
||||
@@ -54,19 +58,21 @@ range-input::before {
|
||||
bottom: 0;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
transform: translate(var(--value-percent, 0%), 0);
|
||||
}
|
||||
|
||||
.value-display {
|
||||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="62" fill="none"><path fill="%2334B9EB" d="M27.3 27.3C25 29.6 17 35.8 17 43v3c0 3 2.5 5 3.2 5.8a6 6 0 1 1-8.5 0C12.6 51 15 49 15 46v-3c0-7.2-8-13.4-10.3-15.7A16 16 0 0 1 16 0a16 16 0 0 1 11.3 27.3z"/><circle cx="16" cy="56" r="1" fill="%235D509E"/></svg>')
|
||||
top center no-repeat;
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
left: 0;
|
||||
left: var(--value-percent, 0%);
|
||||
bottom: 3px;
|
||||
width: 32px;
|
||||
height: 62px;
|
||||
text-align: center;
|
||||
padding: 8px 3px 0;
|
||||
margin: 0 0 0 -16px;
|
||||
filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3));
|
||||
transform-origin: 50% 90%;
|
||||
opacity: 0.0001;
|
||||
transform: scale(0.2);
|
||||
@@ -80,19 +86,6 @@ range-input::before {
|
||||
will-change: transform;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
|
||||
> svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
fill: var(--main-theme-color);
|
||||
}
|
||||
|
||||
> span {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.touch-active + .thumb-wrapper .value-display {
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
import RangeInputElement from './custom-els/RangeInput';
|
||||
import './custom-els/RangeInput';
|
||||
import { linkRef } from 'shared/prerendered-app/util';
|
||||
import { linkRef } from 'shared/initial-app/util';
|
||||
|
||||
interface Props extends preact.JSX.HTMLAttributes {}
|
||||
interface State {}
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
box-sizing: border-box;
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
text-decoration-color: var(--main-theme-color);
|
||||
text-underline-position: under;
|
||||
width: 48px;
|
||||
position: relative;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { h, Component } from 'preact';
|
||||
import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
import { Arrow } from '../../../icons';
|
||||
|
||||
interface Props extends preact.JSX.HTMLAttributes {}
|
||||
interface State {}
|
||||
|
||||
export default class Revealer extends Component<Props, State> {
|
||||
render(props: Props) {
|
||||
return (
|
||||
<div class={style.checkbox}>
|
||||
{/* @ts-ignore - TS bug https://github.com/microsoft/TypeScript/issues/16019 */}
|
||||
<input class={style.realCheckbox} type="checkbox" {...props} />
|
||||
<div class={style.arrow}>
|
||||
<Arrow />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
.checkbox {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
fill: var(--white);
|
||||
transition: transform 200ms ease;
|
||||
transform: rotate(-90deg);
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.real-checkbox {
|
||||
top: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
|
||||
&:checked + .arrow {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
import { Arrow } from 'client/lazy-app/icons';
|
||||
|
||||
interface Props extends preact.JSX.HTMLAttributes {
|
||||
large?: boolean;
|
||||
@@ -19,9 +18,9 @@ export default class Select extends Component<Props, State> {
|
||||
class={`${style.builtinSelect} ${large ? style.large : ''}`}
|
||||
{...otherProps}
|
||||
/>
|
||||
<div class={style.arrow}>
|
||||
<Arrow />
|
||||
</div>
|
||||
<svg class={style.arrow} viewBox="0 0 10 5">
|
||||
<path d="M0 0l5 5 5-5z" />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
}
|
||||
|
||||
.builtin-select {
|
||||
background: var(--black);
|
||||
background: #2f2f2f;
|
||||
border-radius: 4px;
|
||||
font: inherit;
|
||||
padding: 7px 0;
|
||||
padding-right: 25px;
|
||||
padding-left: 10px;
|
||||
padding: 4px 25px 4px 10px;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
@@ -27,7 +25,7 @@
|
||||
|
||||
.large {
|
||||
padding: 10px 35px 10px 10px;
|
||||
background: var(--dark-gray);
|
||||
background: #151515;
|
||||
|
||||
& .arrow {
|
||||
right: 13px;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { h, Component } from 'preact';
|
||||
import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
|
||||
interface Props extends preact.JSX.HTMLAttributes {}
|
||||
interface State {}
|
||||
|
||||
export default class Toggle extends Component<Props, State> {
|
||||
render(props: Props) {
|
||||
return (
|
||||
<div class={style.checkbox}>
|
||||
{/* @ts-ignore - TS bug https://github.com/microsoft/TypeScript/issues/16019 */}
|
||||
<input class={style.realCheckbox} type="checkbox" {...props} />
|
||||
<div class={style.track}>
|
||||
<div class={style.thumbTrack}>
|
||||
<div class={style.thumb}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
.checkbox {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.track {
|
||||
--thumb-size: 14px;
|
||||
background: var(--black);
|
||||
border-radius: 1000px;
|
||||
width: 24px;
|
||||
padding: 3px calc(var(--thumb-size) / 2 + 3px);
|
||||
}
|
||||
|
||||
.thumb {
|
||||
position: relative;
|
||||
width: var(--thumb-size);
|
||||
height: var(--thumb-size);
|
||||
background: var(--less-light-gray);
|
||||
border-radius: 100%;
|
||||
transform: translateX(calc(var(--thumb-size) / -2));
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--main-theme-color);
|
||||
opacity: 0;
|
||||
transition: opacity 200ms ease;
|
||||
}
|
||||
}
|
||||
|
||||
.thumb-track {
|
||||
transition: transform 200ms ease;
|
||||
}
|
||||
|
||||
.real-checkbox {
|
||||
top: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
|
||||
&:checked + .track {
|
||||
.thumb-track {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.thumb::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,20 +14,18 @@ import {
|
||||
} from '../../feature-meta';
|
||||
import Expander from './Expander';
|
||||
import Checkbox from './Checkbox';
|
||||
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';
|
||||
|
||||
interface Props {
|
||||
index: 0 | 1;
|
||||
mobileView: boolean;
|
||||
source?: SourceImage;
|
||||
encoderState?: EncoderState;
|
||||
processorState: ProcessorState;
|
||||
onEncoderTypeChange(index: 0 | 1, newType: OutputType): void;
|
||||
onEncoderOptionsChange(index: 0 | 1, newOptions: EncoderOptions): void;
|
||||
onProcessorOptionsChange(index: 0 | 1, newOptions: ProcessorState): void;
|
||||
onEncoderTypeChange(newType: OutputType): void;
|
||||
onEncoderOptionsChange(newOptions: EncoderOptions): void;
|
||||
onProcessorOptionsChange(newOptions: ProcessorState): void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -75,7 +73,7 @@ export default class Options extends Component<Props, State> {
|
||||
// The select element only has values matching encoder types,
|
||||
// so 'as' is safe here.
|
||||
const type = el.value as OutputType;
|
||||
this.props.onEncoderTypeChange(this.props.index, type);
|
||||
this.props.onEncoderTypeChange(type);
|
||||
};
|
||||
|
||||
private onProcessorEnabledChange = (event: Event) => {
|
||||
@@ -83,31 +81,24 @@ export default class Options extends Component<Props, State> {
|
||||
const processor = el.name.split('.')[0] as keyof ProcessorState;
|
||||
|
||||
this.props.onProcessorOptionsChange(
|
||||
this.props.index,
|
||||
cleanSet(this.props.processorState, `${processor}.enabled`, el.checked),
|
||||
);
|
||||
};
|
||||
|
||||
private onQuantizerOptionsChange = (opts: ProcessorOptions['quantize']) => {
|
||||
this.props.onProcessorOptionsChange(
|
||||
this.props.index,
|
||||
cleanMerge(this.props.processorState, 'quantize', opts),
|
||||
);
|
||||
};
|
||||
|
||||
private onResizeOptionsChange = (opts: ProcessorOptions['resize']) => {
|
||||
this.props.onProcessorOptionsChange(
|
||||
this.props.index,
|
||||
cleanMerge(this.props.processorState, 'resize', opts),
|
||||
);
|
||||
};
|
||||
|
||||
private onEncoderOptionsChange = (newOptions: EncoderOptions) => {
|
||||
this.props.onEncoderOptionsChange(this.props.index, newOptions);
|
||||
};
|
||||
|
||||
render(
|
||||
{ source, encoderState, processorState }: Props,
|
||||
{ source, encoderState, processorState, onEncoderOptionsChange }: Props,
|
||||
{ supportedEncoderMap }: State,
|
||||
) {
|
||||
const encoder = encoderState && encoderMap[encoderState.type];
|
||||
@@ -115,24 +106,18 @@ export default class Options extends Component<Props, State> {
|
||||
encoder && 'Options' in encoder ? encoder.Options : undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
class={
|
||||
style.optionsScroller +
|
||||
' ' +
|
||||
(encoderState ? '' : style.originalImage)
|
||||
}
|
||||
>
|
||||
<div class={style.optionsScroller}>
|
||||
<Expander>
|
||||
{!encoderState ? null : (
|
||||
<div>
|
||||
<h3 class={style.optionsTitle}>Edit</h3>
|
||||
<label class={style.sectionEnabler}>
|
||||
Resize
|
||||
<Toggle
|
||||
<Checkbox
|
||||
name="resize.enable"
|
||||
checked={!!processorState.resize.enabled}
|
||||
onChange={this.onProcessorEnabledChange}
|
||||
/>
|
||||
Resize
|
||||
</label>
|
||||
<Expander>
|
||||
{processorState.resize.enabled ? (
|
||||
@@ -147,12 +132,12 @@ export default class Options extends Component<Props, State> {
|
||||
</Expander>
|
||||
|
||||
<label class={style.sectionEnabler}>
|
||||
Reduce palette
|
||||
<Toggle
|
||||
<Checkbox
|
||||
name="quantize.enable"
|
||||
checked={!!processorState.quantize.enabled}
|
||||
onChange={this.onProcessorEnabledChange}
|
||||
/>
|
||||
Reduce palette
|
||||
</label>
|
||||
<Expander>
|
||||
{processorState.quantize.enabled ? (
|
||||
@@ -195,7 +180,7 @@ export default class Options extends Component<Props, State> {
|
||||
// the correct type, but typescript isn't smart enough.
|
||||
encoderState!.options as any
|
||||
}
|
||||
onChange={this.onEncoderOptionsChange}
|
||||
onChange={onEncoderOptionsChange}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
|
||||
@@ -3,73 +3,56 @@
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
--horizontal-padding: 15px;
|
||||
border-radius: var(--scroller-radius);
|
||||
}
|
||||
|
||||
.options-title {
|
||||
background-color: var(--main-theme-color);
|
||||
color: var(--header-text-color);
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
margin: 0;
|
||||
padding: 10px var(--horizontal-padding);
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
font-size: 1.4rem;
|
||||
border-bottom: 1px solid var(--off-black);
|
||||
transition: all 300ms ease-in-out;
|
||||
transition-property: background-color, color;
|
||||
}
|
||||
|
||||
.original-image .options-title {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
.option-text-first {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 87px 1fr;
|
||||
gap: 0.7em;
|
||||
grid-gap: 0.7em;
|
||||
padding: 10px var(--horizontal-padding);
|
||||
}
|
||||
|
||||
.option-toggle {
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 0.7em;
|
||||
padding: 10px var(--horizontal-padding);
|
||||
}
|
||||
|
||||
.option-reveal {
|
||||
composes: option-toggle;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.option-one-cell {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
padding: 10px var(--horizontal-padding);
|
||||
}
|
||||
|
||||
.option-input-first,
|
||||
.section-enabler {
|
||||
composes: option-toggle;
|
||||
background: var(--dark-gray);
|
||||
padding: 15px var(--horizontal-padding);
|
||||
border-bottom: 1px solid var(--off-black);
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-gap: 0.7em;
|
||||
padding: 10px var(--horizontal-padding);
|
||||
}
|
||||
|
||||
.section-enabler {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.options-section {
|
||||
background: var(--off-black);
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.text-field {
|
||||
background: var(--white);
|
||||
color: var(--black);
|
||||
background: #fff;
|
||||
color: #000;
|
||||
font: inherit;
|
||||
border: none;
|
||||
padding: 6px 0 6px 10px;
|
||||
padding: 2px 0 2px 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
@@ -73,14 +73,9 @@ export default class TwoUp extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this._childrenChange();
|
||||
|
||||
// prettier-ignore
|
||||
this._handle.innerHTML =
|
||||
`<div class="${styles.scrubber}">${
|
||||
`<svg viewBox="0 0 27 20">${
|
||||
`<path class="${styles.arrowLeft}" d="M9.6 0L0 9.6l9.6 9.6z"/>` +
|
||||
`<path class="${styles.arrowRight}" d="M17 19.2l9.5-9.6L16.9 0z"/>`
|
||||
}</svg>
|
||||
`}</div>`;
|
||||
this._handle.innerHTML = `<div class="${
|
||||
styles.scrubber
|
||||
}">${`<svg viewBox="0 0 27 20" fill="currentColor">${'<path d="M17 19.2l9.5-9.6L16.9 0zM9.6 0L0 9.6l9.6 9.6z"/>'}</svg>`}</div>`;
|
||||
|
||||
if (!this._everConnected) {
|
||||
this._resetPosition();
|
||||
|
||||
@@ -2,11 +2,12 @@ two-up {
|
||||
display: grid;
|
||||
position: relative;
|
||||
--split-point: 0;
|
||||
--track-color: rgb(0 0 0 / 0.6);
|
||||
--thumb-background: var(--black);
|
||||
--accent-color: #777;
|
||||
--track-color: var(--accent-color);
|
||||
--thumb-background: #fff;
|
||||
--thumb-color: var(--accent-color);
|
||||
--thumb-size: 62px;
|
||||
--bar-size: 9px;
|
||||
--bar-size: 6px;
|
||||
--bar-touch-size: 30px;
|
||||
}
|
||||
|
||||
@@ -36,6 +37,8 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
|
||||
height: 100%;
|
||||
width: var(--bar-size);
|
||||
margin: 0 auto;
|
||||
box-shadow: inset calc(var(--bar-size) / 2) 0 0 rgba(0, 0, 0, 0.1),
|
||||
0 1px 4px rgba(0, 0, 0, 0.4);
|
||||
background: var(--track-color);
|
||||
}
|
||||
|
||||
@@ -44,11 +47,14 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform-origin: 50% 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: var(--thumb-size);
|
||||
height: var(--thumb-size);
|
||||
height: calc(var(--thumb-size) * 0.9);
|
||||
background: var(--thumb-background);
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: var(--thumb-size);
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
||||
color: var(--thumb-color);
|
||||
box-sizing: border-box;
|
||||
padding: 0 calc(var(--thumb-size) * 0.24);
|
||||
@@ -58,14 +64,6 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.arrow-left {
|
||||
fill: var(--pink);
|
||||
}
|
||||
|
||||
.arrow-right {
|
||||
fill: var(--blue);
|
||||
}
|
||||
|
||||
two-up[orientation='vertical'] .two-up-handle {
|
||||
width: auto;
|
||||
height: var(--bar-touch-size);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { h, createRef, Component, Fragment } from 'preact';
|
||||
import { h, Component } from 'preact';
|
||||
import type PinchZoom from './custom-els/PinchZoom';
|
||||
import type { ScaleToOpts } from './custom-els/PinchZoom';
|
||||
import './custom-els/PinchZoom';
|
||||
@@ -10,36 +10,32 @@ import {
|
||||
ToggleBackgroundIcon,
|
||||
AddIcon,
|
||||
RemoveIcon,
|
||||
BackIcon,
|
||||
ToggleBackgroundActiveIcon,
|
||||
RotateIcon,
|
||||
MoreIcon,
|
||||
} from '../../icons';
|
||||
import { twoUpHandle } from './custom-els/TwoUp/styles.css';
|
||||
import type { PreprocessorState } from '../../feature-meta';
|
||||
import { cleanSet } from '../../util/clean-modify';
|
||||
import type { SourceImage } from '../../Compress';
|
||||
import { linkRef } from 'shared/prerendered-app/util';
|
||||
import Flyout from '../Flyout';
|
||||
import { linkRef } from 'shared/initial-app/util';
|
||||
|
||||
interface Props {
|
||||
source?: SourceImage;
|
||||
preprocessorState?: PreprocessorState;
|
||||
hidden?: boolean;
|
||||
mobileView: boolean;
|
||||
leftCompressed?: ImageData;
|
||||
rightCompressed?: ImageData;
|
||||
leftImgContain: boolean;
|
||||
rightImgContain: boolean;
|
||||
onPreprocessorChange?: (newState: PreprocessorState) => void;
|
||||
onShowPreprocessorTransforms?: () => void;
|
||||
onToggleBackground?: () => void;
|
||||
onBack: () => void;
|
||||
onPreprocessorChange: (newState: PreprocessorState) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
scale: number;
|
||||
editingScale: boolean;
|
||||
altBackground: boolean;
|
||||
transform: boolean;
|
||||
menuOpen: boolean;
|
||||
}
|
||||
|
||||
const scaleToOpts: ScaleToOpts = {
|
||||
@@ -54,15 +50,12 @@ export default class Output extends Component<Props, State> {
|
||||
scale: 1,
|
||||
editingScale: false,
|
||||
altBackground: false,
|
||||
transform: false,
|
||||
menuOpen: false,
|
||||
};
|
||||
canvasLeft?: HTMLCanvasElement;
|
||||
canvasRight?: HTMLCanvasElement;
|
||||
pinchZoomLeft?: PinchZoom;
|
||||
pinchZoomRight?: PinchZoom;
|
||||
scaleInput?: HTMLInputElement;
|
||||
flyout = createRef<Flyout>();
|
||||
retargetedEvents = new WeakSet<Event>();
|
||||
|
||||
componentDidMount() {
|
||||
@@ -153,6 +146,12 @@ export default class Output extends Component<Props, State> {
|
||||
return props.rightCompressed || (props.source && props.source.preprocessed);
|
||||
}
|
||||
|
||||
private toggleBackground = () => {
|
||||
this.setState({
|
||||
altBackground: !this.state.altBackground,
|
||||
});
|
||||
};
|
||||
|
||||
private zoomIn = () => {
|
||||
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
||||
this.pinchZoomLeft.scaleTo(this.state.scale * 1.25, scaleToOpts);
|
||||
@@ -163,36 +162,17 @@ export default class Output extends Component<Props, State> {
|
||||
this.pinchZoomLeft.scaleTo(this.state.scale / 1.25, scaleToOpts);
|
||||
};
|
||||
|
||||
private fitToViewport = () => {
|
||||
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
||||
const img = this.props.source?.preprocessed;
|
||||
if (!img) return;
|
||||
const scale = Number(
|
||||
Math.min(
|
||||
(window.innerWidth - 20) / img.width,
|
||||
(window.innerHeight - 20) / img.height,
|
||||
).toFixed(2),
|
||||
private onRotateClick = () => {
|
||||
const { preprocessorState: inputProcessorState } = this.props;
|
||||
if (!inputProcessorState) return;
|
||||
|
||||
const newState = cleanSet(
|
||||
inputProcessorState,
|
||||
'rotate.rotate',
|
||||
(inputProcessorState.rotate.rotate + 90) % 360,
|
||||
);
|
||||
this.pinchZoomLeft.scaleTo(Number(scale.toFixed(2)), scaleToOpts);
|
||||
this.recenter();
|
||||
// this.hideMenu();
|
||||
};
|
||||
|
||||
private zoomTo2x = () => {
|
||||
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
||||
this.pinchZoomLeft.scaleTo(0.5, scaleToOpts);
|
||||
this.recenter();
|
||||
};
|
||||
|
||||
private recenter = () => {
|
||||
const img = this.props.source?.preprocessed;
|
||||
if (!img || !this.pinchZoomLeft) return;
|
||||
let scale = this.pinchZoomLeft.scale;
|
||||
this.pinchZoomLeft.setTransform({
|
||||
x: (img.width - img.width * scale) / 2,
|
||||
y: (img.height - img.height * scale) / 2,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
this.props.onPreprocessorChange(newState);
|
||||
};
|
||||
|
||||
private onScaleValueFocus = () => {
|
||||
@@ -275,16 +255,8 @@ export default class Output extends Component<Props, State> {
|
||||
};
|
||||
|
||||
render(
|
||||
{
|
||||
source,
|
||||
mobileView,
|
||||
hidden,
|
||||
leftImgContain,
|
||||
rightImgContain,
|
||||
onShowPreprocessorTransforms,
|
||||
onToggleBackground,
|
||||
}: Props,
|
||||
{ scale, editingScale }: State,
|
||||
{ mobileView, leftImgContain, rightImgContain, source, onBack }: Props,
|
||||
{ scale, editingScale, altBackground }: State,
|
||||
) {
|
||||
const leftDraw = this.leftDrawable();
|
||||
const rightDraw = this.rightDrawable();
|
||||
@@ -292,60 +264,63 @@ export default class Output extends Component<Props, State> {
|
||||
const originalImage = source && source.preprocessed;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div class={style.output} hidden={hidden}>
|
||||
<two-up
|
||||
legacy-clip-compat
|
||||
class={style.twoUp}
|
||||
orientation={mobileView ? 'vertical' : 'horizontal'}
|
||||
// Event redirecting. See onRetargetableEvent.
|
||||
onTouchStartCapture={this.onRetargetableEvent}
|
||||
onTouchEndCapture={this.onRetargetableEvent}
|
||||
onTouchMoveCapture={this.onRetargetableEvent}
|
||||
onPointerDownCapture={this.onRetargetableEvent}
|
||||
onMouseDownCapture={this.onRetargetableEvent}
|
||||
onWheelCapture={this.onRetargetableEvent}
|
||||
<div
|
||||
class={`${style.output} ${altBackground ? style.altBackground : ''}`}
|
||||
>
|
||||
<two-up
|
||||
legacy-clip-compat
|
||||
class={style.twoUp}
|
||||
orientation={mobileView ? 'vertical' : 'horizontal'}
|
||||
// Event redirecting. See onRetargetableEvent.
|
||||
onTouchStartCapture={this.onRetargetableEvent}
|
||||
onTouchEndCapture={this.onRetargetableEvent}
|
||||
onTouchMoveCapture={this.onRetargetableEvent}
|
||||
onPointerDownCapture={this.onRetargetableEvent}
|
||||
onMouseDownCapture={this.onRetargetableEvent}
|
||||
onWheelCapture={this.onRetargetableEvent}
|
||||
>
|
||||
<pinch-zoom
|
||||
class={style.pinchZoom}
|
||||
onChange={this.onPinchZoomLeftChange}
|
||||
ref={linkRef(this, 'pinchZoomLeft')}
|
||||
>
|
||||
<pinch-zoom
|
||||
class={style.pinchZoom}
|
||||
onChange={this.onPinchZoomLeftChange}
|
||||
ref={linkRef(this, 'pinchZoomLeft')}
|
||||
>
|
||||
<canvas
|
||||
class={style.pinchTarget}
|
||||
ref={linkRef(this, 'canvasLeft')}
|
||||
width={leftDraw && leftDraw.width}
|
||||
height={leftDraw && leftDraw.height}
|
||||
style={{
|
||||
width: originalImage ? originalImage.width : '',
|
||||
height: originalImage ? originalImage.height : '',
|
||||
objectFit: leftImgContain ? 'contain' : undefined,
|
||||
}}
|
||||
/>
|
||||
</pinch-zoom>
|
||||
<pinch-zoom
|
||||
class={style.pinchZoom}
|
||||
ref={linkRef(this, 'pinchZoomRight')}
|
||||
>
|
||||
<canvas
|
||||
class={style.pinchTarget}
|
||||
ref={linkRef(this, 'canvasRight')}
|
||||
width={rightDraw && rightDraw.width}
|
||||
height={rightDraw && rightDraw.height}
|
||||
style={{
|
||||
width: originalImage ? originalImage.width : '',
|
||||
height: originalImage ? originalImage.height : '',
|
||||
objectFit: rightImgContain ? 'contain' : undefined,
|
||||
}}
|
||||
/>
|
||||
</pinch-zoom>
|
||||
</two-up>
|
||||
<canvas
|
||||
class={style.pinchTarget}
|
||||
ref={linkRef(this, 'canvasLeft')}
|
||||
width={leftDraw && leftDraw.width}
|
||||
height={leftDraw && leftDraw.height}
|
||||
style={{
|
||||
width: originalImage ? originalImage.width : '',
|
||||
height: originalImage ? originalImage.height : '',
|
||||
objectFit: leftImgContain ? 'contain' : '',
|
||||
}}
|
||||
/>
|
||||
</pinch-zoom>
|
||||
<pinch-zoom
|
||||
class={style.pinchZoom}
|
||||
ref={linkRef(this, 'pinchZoomRight')}
|
||||
>
|
||||
<canvas
|
||||
class={style.pinchTarget}
|
||||
ref={linkRef(this, 'canvasRight')}
|
||||
width={rightDraw && rightDraw.width}
|
||||
height={rightDraw && rightDraw.height}
|
||||
style={{
|
||||
width: originalImage ? originalImage.width : '',
|
||||
height: originalImage ? originalImage.height : '',
|
||||
objectFit: rightImgContain ? 'contain' : '',
|
||||
}}
|
||||
/>
|
||||
</pinch-zoom>
|
||||
</two-up>
|
||||
|
||||
<div class={style.back}>
|
||||
<button class={style.button} onClick={onBack}>
|
||||
<BackIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class={style.controls}
|
||||
hidden={hidden}
|
||||
>
|
||||
<div class={style.controls}>
|
||||
<div class={style.zoomControls}>
|
||||
<button class={style.button} onClick={this.zoomOut}>
|
||||
<RemoveIcon />
|
||||
@@ -374,39 +349,29 @@ export default class Output extends Component<Props, State> {
|
||||
<button class={style.button} onClick={this.zoomIn}>
|
||||
<AddIcon />
|
||||
</button>
|
||||
|
||||
<Flyout
|
||||
class={style.menu}
|
||||
showing={hidden ? false : undefined}
|
||||
anchor="right"
|
||||
direction={mobileView ? 'down' : 'up'}
|
||||
toggle={
|
||||
<button class={`${style.button} ${style.moreButton}`}>
|
||||
<MoreIcon />
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div class={style.buttonsNoWrap}>
|
||||
<button
|
||||
class={style.button}
|
||||
onClick={this.onRotateClick}
|
||||
title="Rotate image"
|
||||
>
|
||||
<button class={style.button} onClick={onShowPreprocessorTransforms}>
|
||||
<RotateIcon /> Rotate & Transform
|
||||
</button>
|
||||
<button class={style.button} onClick={this.fitToViewport}>
|
||||
Fit to viewport
|
||||
</button>
|
||||
<button class={style.button} onClick={this.zoomTo2x}>
|
||||
Simulate retina
|
||||
</button>
|
||||
<button class={style.button} onClick={this.recenter}>
|
||||
Re-center
|
||||
</button>
|
||||
<button class={style.button} onClick={onToggleBackground}>
|
||||
<RotateIcon />
|
||||
</button>
|
||||
<button
|
||||
class={`${style.button} ${altBackground ? style.active : ''}`}
|
||||
onClick={this.toggleBackground}
|
||||
title="Change canvas color"
|
||||
>
|
||||
{altBackground ? (
|
||||
<ToggleBackgroundActiveIcon />
|
||||
) : (
|
||||
<ToggleBackgroundIcon />
|
||||
{' '}
|
||||
Change canvas color
|
||||
</button>
|
||||
</Flyout>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
.output {
|
||||
display: contents;
|
||||
composes: abs-fill from '../../../../shared/initial-app/util.css';
|
||||
|
||||
&[hidden] {
|
||||
display: none;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #000;
|
||||
opacity: 0;
|
||||
transition: opacity 500ms ease;
|
||||
}
|
||||
|
||||
&.alt-background::before {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.two-up {
|
||||
composes: abs-fill from global;
|
||||
composes: abs-fill from '../../../../shared/initial-app/util.css';
|
||||
--accent-color: var(--button-fg);
|
||||
}
|
||||
|
||||
.pinch-zoom {
|
||||
composes: abs-fill from global;
|
||||
composes: abs-fill from '../../../../shared/initial-app/util.css';
|
||||
outline: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -29,55 +41,42 @@
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
grid-area: header;
|
||||
align-self: center;
|
||||
padding: 9px 66px;
|
||||
/* Had to disable containment because of the overflow menu. */
|
||||
/*
|
||||
contain: content;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 9px 84px;
|
||||
overflow: hidden;
|
||||
*/
|
||||
transition: transform 500ms ease;
|
||||
flex-wrap: wrap;
|
||||
contain: content;
|
||||
|
||||
/* Allow clicks to fall through to the pinch zoom area */
|
||||
pointer-events: none;
|
||||
|
||||
& > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 860px) {
|
||||
padding: 9px;
|
||||
top: auto;
|
||||
left: 320px;
|
||||
right: 320px;
|
||||
bottom: 0;
|
||||
flex-wrap: wrap-reverse;
|
||||
grid-area: viewportOpts;
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
&[hidden] {
|
||||
visibility: visible;
|
||||
transform: translateY(-200%);
|
||||
|
||||
@media (min-width: 860px) {
|
||||
transform: translateY(200%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-controls {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
|
||||
& > :not(:first-child) {
|
||||
& :not(:first-child) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
& > :not(:nth-last-child(2)) {
|
||||
& :not(:last-child) {
|
||||
margin-right: 0;
|
||||
border-right-width: 0;
|
||||
border-top-right-radius: 0;
|
||||
@@ -91,72 +90,72 @@
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
margin: 4px;
|
||||
background-color: rgba(29, 29, 29, 0.92);
|
||||
border: 1px solid rgba(0, 0, 0, 0.67);
|
||||
border-radius: 6px;
|
||||
line-height: 1.1;
|
||||
background-color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 5px;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
height: 39px;
|
||||
height: 36px;
|
||||
padding: 0 8px;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
/* box-shadow: 0 0 0 2px var(--hot-pink); */
|
||||
box-shadow: 0 0 0 2px #fff;
|
||||
box-shadow: 0 0 0 2px var(--button-fg);
|
||||
outline: none;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
color: #fff;
|
||||
color: var(--button-fg);
|
||||
|
||||
&:hover {
|
||||
background: rgba(50, 50, 50, 0.92);
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(72, 72, 72, 0.92);
|
||||
background: #34b9eb;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
background: #32a3ce;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.zoom {
|
||||
color: #625e80;
|
||||
cursor: text;
|
||||
width: 7rem;
|
||||
width: 6em;
|
||||
font: inherit;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
|
||||
&:focus {
|
||||
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2), 0 0 0 2px #fff;
|
||||
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2), 0 0 0 2px var(--button-fg);
|
||||
}
|
||||
}
|
||||
span.zoom {
|
||||
color: #939393;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 100;
|
||||
}
|
||||
input.zoom {
|
||||
font-size: 1.2rem;
|
||||
letter-spacing: 0.05rem;
|
||||
font-weight: 700;
|
||||
text-indent: 3px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.zoom-value {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin: 0 3px 0 0;
|
||||
padding: 0 2px;
|
||||
font-size: 1.2rem;
|
||||
letter-spacing: 0.05rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
color: #888;
|
||||
border-bottom: 1px dashed #999;
|
||||
}
|
||||
|
||||
.back {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 9px;
|
||||
}
|
||||
|
||||
.buttons-no-wrap {
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
@@ -165,64 +164,3 @@ input.zoom {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/** Three-dot menu */
|
||||
.moreButton {
|
||||
padding: 0 4px;
|
||||
|
||||
& > svg {
|
||||
transform-origin: center;
|
||||
transition: transform 200ms ease;
|
||||
}
|
||||
}
|
||||
|
||||
[data-flyout-open] {
|
||||
.moreButton {
|
||||
background: rgba(82, 82, 82, 0.92);
|
||||
|
||||
& > svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(50, 50, 50, 0.4);
|
||||
backdrop-filter: blur(2px) contrast(70%);
|
||||
animation: menuShimFadeIn 350ms ease forwards 1;
|
||||
will-change: opacity;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes menuShimFadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
button {
|
||||
margin: 8px 0;
|
||||
border-radius: 2rem;
|
||||
padding: 0 16px;
|
||||
|
||||
& > svg {
|
||||
position: relative;
|
||||
left: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.8rem;
|
||||
color: #fff;
|
||||
margin: 8px 4px;
|
||||
padding: 10px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
CopyAcrossIcon,
|
||||
CopyAcrossIconProps,
|
||||
} from 'client/lazy-app/icons';
|
||||
import 'shared/custom-els/loading-spinner';
|
||||
import 'shared/initial-app/custom-els/loading-spinner';
|
||||
import { SourceImage } from '../';
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
.copy-to-other {
|
||||
grid-row: 1;
|
||||
grid-column: copy-button;
|
||||
composes: unbutton from global;
|
||||
composes: unbutton from '../../../../shared/initial-app/util.css';
|
||||
composes: download;
|
||||
|
||||
background: #656565;
|
||||
|
||||
@@ -1,388 +0,0 @@
|
||||
import { h, Component, ComponentChildren } from 'preact';
|
||||
import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
import { shallowEqual } from 'client/lazy-app/util';
|
||||
|
||||
export interface CropBox {
|
||||
left: number;
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
}
|
||||
|
||||
// Minimum CropBox size
|
||||
const MIN_SIZE = 2;
|
||||
|
||||
export interface Props {
|
||||
size: { width: number; height: number };
|
||||
scale?: number;
|
||||
lockAspect?: boolean;
|
||||
crop: CropBox;
|
||||
onChange?(crop: CropBox): void;
|
||||
}
|
||||
|
||||
type Edge = keyof CropBox;
|
||||
|
||||
interface PointerTrack {
|
||||
x: number;
|
||||
y: number;
|
||||
edges: { edge: Edge; value: number }[];
|
||||
}
|
||||
|
||||
interface State {
|
||||
crop: CropBox;
|
||||
pan: boolean;
|
||||
}
|
||||
|
||||
export default class Cropper extends Component<Props, State> {
|
||||
private pointers = new Map<number, PointerTrack>();
|
||||
|
||||
state = {
|
||||
crop: this.normalizeCrop({ ...this.props.crop }),
|
||||
pan: false,
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps: Props, nextState: State) {
|
||||
if (!shallowEqual(nextState, this.state)) return true;
|
||||
const { size, scale, lockAspect, crop } = this.props;
|
||||
return (
|
||||
size.width !== nextProps.size.width ||
|
||||
size.height !== nextProps.size.height ||
|
||||
scale !== nextProps.scale ||
|
||||
lockAspect !== nextProps.lockAspect ||
|
||||
!shallowEqual(crop, nextProps.crop)
|
||||
);
|
||||
}
|
||||
|
||||
componentWillReceiveProps({ crop }: Props, nextState: State) {
|
||||
const current = nextState.crop || this.state.crop;
|
||||
if (crop !== this.props.crop && !shallowEqual(crop, current)) {
|
||||
// this.setState({ crop: nextProps.crop });
|
||||
this.setCrop(crop);
|
||||
}
|
||||
}
|
||||
|
||||
private normalizeCrop(crop: CropBox) {
|
||||
crop.left = Math.round(Math.max(0, crop.left));
|
||||
crop.top = Math.round(Math.max(0, crop.top));
|
||||
crop.right = Math.round(Math.max(0, crop.right));
|
||||
crop.bottom = Math.round(Math.max(0, crop.bottom));
|
||||
return crop;
|
||||
}
|
||||
|
||||
private setCrop(cropUpdate: Partial<CropBox>) {
|
||||
const crop = this.normalizeCrop({ ...this.state.crop, ...cropUpdate });
|
||||
// ignore crop updates that normalize to the same values
|
||||
const old = this.state.crop;
|
||||
if (
|
||||
crop.left === old.left &&
|
||||
crop.right === old.right &&
|
||||
crop.top === old.top &&
|
||||
crop.bottom === old.bottom
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// crop.left = Math.max(0, crop.left) | 0;
|
||||
// crop.top = Math.max(0, crop.top) | 0;
|
||||
// crop.right = Math.max(0, crop.right) | 0;
|
||||
// crop.bottom = Math.max(0, crop.bottom) | 0;
|
||||
this.setState({ crop });
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(crop);
|
||||
}
|
||||
}
|
||||
|
||||
private onPointerDown = (event: PointerEvent) => {
|
||||
if (event.button !== 0 || this.state.pan) return;
|
||||
|
||||
const target = event.target as SVGElement;
|
||||
const edgeAttr = target.getAttribute('data-edge');
|
||||
if (edgeAttr) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const edges = edgeAttr.split(/ *, */) as Edge[];
|
||||
// console.log(this.props.lockAspect);
|
||||
if (this.props.lockAspect && edges.length === 1) return;
|
||||
|
||||
this.pointers.set(event.pointerId, {
|
||||
x: event.x,
|
||||
y: event.y,
|
||||
edges: edges.map((edge) => ({ edge, value: this.state.crop[edge] })),
|
||||
});
|
||||
target.setPointerCapture(event.pointerId);
|
||||
}
|
||||
};
|
||||
|
||||
private onPointerMove = (event: PointerEvent) => {
|
||||
const target = event.target as SVGElement;
|
||||
const down = this.pointers.get(event.pointerId);
|
||||
if (down && target.hasPointerCapture(event.pointerId)) {
|
||||
const { size } = this.props;
|
||||
const oldCrop = this.state.crop;
|
||||
const aspect =
|
||||
(size.width - oldCrop.left - oldCrop.right) /
|
||||
(size.height - oldCrop.top - oldCrop.bottom);
|
||||
const scale = this.props.scale || 1;
|
||||
let dx = (event.x - down.x) / scale;
|
||||
let dy = (event.y - down.y) / scale;
|
||||
// console.log(this.props.lockAspect, aspect);
|
||||
if (this.props.lockAspect) {
|
||||
const dir = (dx + dy) / 2;
|
||||
dx = dir * aspect;
|
||||
dy = dir / aspect;
|
||||
}
|
||||
const crop: Partial<CropBox> = {};
|
||||
for (const { edge, value } of down.edges) {
|
||||
let edgeValue = value;
|
||||
switch (edge) {
|
||||
case 'left':
|
||||
edgeValue += dx;
|
||||
break;
|
||||
case 'right':
|
||||
edgeValue -= dx;
|
||||
break;
|
||||
case 'top':
|
||||
edgeValue += dy;
|
||||
break;
|
||||
case 'bottom':
|
||||
edgeValue -= dy;
|
||||
break;
|
||||
}
|
||||
crop[edge] = edgeValue;
|
||||
}
|
||||
// Prevent MOVE from resizing the cropbox:
|
||||
if (crop.left && crop.right) {
|
||||
if (crop.left < 0) crop.right += crop.left;
|
||||
if (crop.right < 0) crop.left += crop.right;
|
||||
} else {
|
||||
// enforce minimum 1px cropbox width
|
||||
if (crop.left)
|
||||
crop.left = Math.min(
|
||||
crop.left,
|
||||
size.width - oldCrop.right - MIN_SIZE,
|
||||
);
|
||||
if (crop.right)
|
||||
crop.right = Math.min(
|
||||
crop.right,
|
||||
size.width - oldCrop.left - MIN_SIZE,
|
||||
);
|
||||
}
|
||||
if (crop.top && crop.bottom) {
|
||||
if (crop.top < 0) crop.bottom += crop.top;
|
||||
if (crop.bottom < 0) crop.top += crop.bottom;
|
||||
} else {
|
||||
// enforce minimum 1px cropbox height
|
||||
if (crop.top)
|
||||
crop.top = Math.min(
|
||||
crop.top,
|
||||
size.height - oldCrop.bottom - MIN_SIZE,
|
||||
);
|
||||
if (crop.bottom)
|
||||
crop.bottom = Math.min(
|
||||
crop.bottom,
|
||||
size.height - oldCrop.top - MIN_SIZE,
|
||||
);
|
||||
}
|
||||
this.setCrop(crop);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
private onPointerUp = (event: PointerEvent) => {
|
||||
const target = event.target as SVGElement;
|
||||
const down = this.pointers.get(event.pointerId);
|
||||
if (down && target.hasPointerCapture(event.pointerId)) {
|
||||
this.onPointerMove(event);
|
||||
target.releasePointerCapture(event.pointerId);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.pointers.delete(event.pointerId);
|
||||
}
|
||||
};
|
||||
|
||||
private onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === ' ') {
|
||||
if (!this.state.pan) {
|
||||
this.setState({ pan: true });
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
private onKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === ' ') this.setState({ pan: false });
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
addEventListener('keydown', this.onKeyDown);
|
||||
addEventListener('keyup', this.onKeyUp);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
addEventListener('keydown', this.onKeyDown);
|
||||
addEventListener('keyup', this.onKeyUp);
|
||||
}
|
||||
|
||||
render({ size, scale }: Props, { crop, pan }: State) {
|
||||
const x = crop.left;
|
||||
const y = crop.top;
|
||||
const width = size.width - crop.left - crop.right;
|
||||
const height = size.height - crop.top - crop.bottom;
|
||||
// const x = crop.left.toFixed(2);
|
||||
// const y = crop.top.toFixed(2);
|
||||
// const width = (size.width - crop.left - crop.right).toFixed(2);
|
||||
// const height = (size.height - crop.top - crop.bottom).toFixed(2);
|
||||
|
||||
return (
|
||||
<svg
|
||||
class={`${style.cropper} ${pan ? style.pan : ''}`}
|
||||
width={size.width + 20}
|
||||
height={size.height + 20}
|
||||
viewBox={`-10 -10 ${size.width + 20} ${size.height + 20}`}
|
||||
style={{
|
||||
// this is hack to force style invalidation in Chrome
|
||||
zoom: (scale || 1).toFixed(3),
|
||||
}}
|
||||
onPointerDown={this.onPointerDown}
|
||||
onPointerMove={this.onPointerMove}
|
||||
onPointerUp={this.onPointerUp}
|
||||
>
|
||||
<defs>
|
||||
{/*
|
||||
<clipPath id="bg">
|
||||
<rect x={x} y={y} width={width} height={height} />
|
||||
</clipPath>
|
||||
*/}
|
||||
{/*
|
||||
<filter id="shadow" x="-2" y="-2" width="4" height="4">
|
||||
<feDropShadow
|
||||
dx="0"
|
||||
dy="0.5"
|
||||
stdDeviation="1.5"
|
||||
flood-color="#000"
|
||||
/>
|
||||
</filter>
|
||||
<filter id="shadow2" x="-2" y="-2" width="4" height="4">
|
||||
<feDropShadow
|
||||
dx="0"
|
||||
dy="0.25"
|
||||
stdDeviation="0.5"
|
||||
flood-color="rgba(0,0,0,0.5)"
|
||||
/>
|
||||
</filter>
|
||||
*/}
|
||||
</defs>
|
||||
<rect
|
||||
class={style.background}
|
||||
width={size.width}
|
||||
height={size.height}
|
||||
// mask="url(#bg)"
|
||||
// clip-path="url(#bg)"
|
||||
// style={{
|
||||
// clipPath: `polygon(0 0, 0 100%, 100% 100%, 100% 0, 0 0, ${x}px ${y}px, ${x+width}px ${y}px, ${x+width}px ${y+height}px, ${x}px ${y+height}px, ${x}px ${y}px)`
|
||||
// }}
|
||||
clip-path={`polygon(0 0, 0 100%, 100% 100%, 100% 0, 0 0, ${x}px ${y}px, ${
|
||||
x + width
|
||||
}px ${y}px, ${x + width}px ${y + height}px, ${x}px ${
|
||||
y + height
|
||||
}px, ${x}px ${y}px)`}
|
||||
/>
|
||||
<svg x={x} y={y} width={width} height={height}>
|
||||
<Freezer>
|
||||
<rect
|
||||
id="box"
|
||||
class={style.cropbox}
|
||||
data-edge="left,right,top,bottom"
|
||||
width="100%"
|
||||
height="100%"
|
||||
// filter="url(#shadow2)"
|
||||
/>
|
||||
|
||||
<rect class={style.edge} data-edge="top" width="100%" />
|
||||
<rect class={style.edge} data-edge="bottom" width="100%" y="100%" />
|
||||
<rect class={style.edge} data-edge="left" height="100%" />
|
||||
<rect class={style.edge} data-edge="right" height="100%" x="100%" />
|
||||
|
||||
<circle
|
||||
class={style.corner}
|
||||
data-edge="left,top"
|
||||
// filter="url(#shadow)"
|
||||
/>
|
||||
<circle
|
||||
class={style.corner}
|
||||
data-edge="right,top"
|
||||
cx="100%"
|
||||
// filter="url(#shadow)"
|
||||
/>
|
||||
<circle
|
||||
class={style.corner}
|
||||
data-edge="right,bottom"
|
||||
cx="100%"
|
||||
cy="100%"
|
||||
// filter="url(#shadow)"
|
||||
/>
|
||||
<circle
|
||||
class={style.corner}
|
||||
data-edge="left,bottom"
|
||||
cy="100%"
|
||||
// filter="url(#shadow)"
|
||||
/>
|
||||
</Freezer>
|
||||
</svg>
|
||||
{/*
|
||||
<rect
|
||||
id="box"
|
||||
class={style.cropbox}
|
||||
data-edge="left,right,top,bottom"
|
||||
x={x}
|
||||
y={y}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
<rect
|
||||
class={`${style.edge} ${style.top}`}
|
||||
data-edge="top"
|
||||
x={x}
|
||||
y={y}
|
||||
width={width}
|
||||
/>
|
||||
<rect
|
||||
class={`${style.edge} ${style.bottom}`}
|
||||
data-edge="bottom"
|
||||
x={x}
|
||||
y={size.height - crop.bottom}
|
||||
width={width}
|
||||
/>
|
||||
<rect
|
||||
class={`${style.edge} ${style.left}`}
|
||||
data-edge="left"
|
||||
x={x}
|
||||
y={y}
|
||||
height={height}
|
||||
/>
|
||||
<rect
|
||||
class={`${style.edge} ${style.right}`}
|
||||
data-edge="right"
|
||||
x={size.width - crop.right}
|
||||
y={y}
|
||||
height={height}
|
||||
/>
|
||||
*/}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface FreezerProps {
|
||||
children: ComponentChildren;
|
||||
}
|
||||
class Freezer extends Component<FreezerProps> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render({ children }: FreezerProps) {
|
||||
return children;
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
.cropper {
|
||||
position: absolute;
|
||||
left: calc(-10px / var(--scale, 1));
|
||||
top: calc(-10px / var(--scale, 1));
|
||||
right: calc(-10px / var(--scale, 1));
|
||||
bottom: calc(-10px / var(--scale, 1));
|
||||
shape-rendering: crispedges;
|
||||
overflow: hidden;
|
||||
contain: strict;
|
||||
transform-origin: 0 0;
|
||||
transform: scale(calc(1 / var(--scale))) !important;
|
||||
zoom: var(--scale, 1);
|
||||
|
||||
&.pan {
|
||||
cursor: grabbing;
|
||||
& * {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
& > svg {
|
||||
margin: -10px;
|
||||
padding: 10px;
|
||||
overflow: visible;
|
||||
contain: strict;
|
||||
/* overflow: visible; */
|
||||
}
|
||||
}
|
||||
|
||||
.background {
|
||||
pointer-events: none;
|
||||
fill: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.cropbox {
|
||||
fill: none;
|
||||
stroke: white;
|
||||
stroke-width: calc(1.5 / var(--scale, 1));
|
||||
stroke-dasharray: calc(5 / var(--scale, 1)), calc(5 / var(--scale, 1));
|
||||
stroke-dashoffset: 50%;
|
||||
/* Accept pointer input even though this is unpainted transparent */
|
||||
pointer-events: all;
|
||||
cursor: move;
|
||||
|
||||
/* animation: ants 1s linear forwards infinite; */
|
||||
}
|
||||
/*
|
||||
@keyframes ants {
|
||||
0% { stroke-dashoffset: 0; }
|
||||
100% { stroke-dashoffset: -12; }
|
||||
}
|
||||
*/
|
||||
|
||||
.edge {
|
||||
fill: #aaa;
|
||||
opacity: 0;
|
||||
transition: opacity 250ms ease;
|
||||
z-index: 2;
|
||||
pointer-events: all;
|
||||
--edge-width: calc(10px / var(--scale, 1));
|
||||
|
||||
@media (max-width: 779px) {
|
||||
--edge-width: calc(20px / var(--scale, 1));
|
||||
fill: rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
&[data-edge='left'],
|
||||
&[data-edge='right'] {
|
||||
cursor: ew-resize;
|
||||
transform: translate(calc(var(--edge-width, 10px) / -2), 0);
|
||||
width: var(--edge-width, 10px);
|
||||
}
|
||||
&[data-edge='top'],
|
||||
&[data-edge='bottom'] {
|
||||
cursor: ns-resize;
|
||||
transform: translate(0, calc(var(--edge-width, 10px) / -2));
|
||||
height: var(--edge-width, 10px);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
opacity: 0.1;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.corner {
|
||||
r: calc(4 / var(--scale, 1));
|
||||
stroke-width: calc(4 / var(--scale, 1));
|
||||
stroke: rgba(225, 225, 225, 0.01);
|
||||
fill: white;
|
||||
shape-rendering: geometricprecision;
|
||||
pointer-events: all;
|
||||
transition: fill 250ms ease, stroke 250ms ease;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
stroke: rgba(225, 225, 225, 0.5);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@media (max-width: 779px) {
|
||||
r: calc(10 / var(--scale, 1));
|
||||
stroke-width: calc(2 / var(--scale, 1));
|
||||
}
|
||||
|
||||
&[data-edge='left,top'] {
|
||||
cursor: nw-resize;
|
||||
}
|
||||
&[data-edge='right,top'] {
|
||||
cursor: ne-resize;
|
||||
}
|
||||
&[data-edge='right,bottom'] {
|
||||
cursor: se-resize;
|
||||
}
|
||||
&[data-edge='left,bottom'] {
|
||||
cursor: sw-resize;
|
||||
}
|
||||
}
|
||||
@@ -1,617 +0,0 @@
|
||||
import {
|
||||
h,
|
||||
Component,
|
||||
Fragment,
|
||||
createRef,
|
||||
FunctionComponent,
|
||||
ComponentChildren,
|
||||
} from 'preact';
|
||||
import type {
|
||||
default as PinchZoom,
|
||||
ScaleToOpts,
|
||||
} from '../Output/custom-els/PinchZoom';
|
||||
import '../Output/custom-els/PinchZoom';
|
||||
import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
import {
|
||||
AddIcon,
|
||||
CheckmarkIcon,
|
||||
CompareIcon,
|
||||
FlipHorizontallyIcon,
|
||||
FlipVerticallyIcon,
|
||||
RemoveIcon,
|
||||
RotateClockwiseIcon,
|
||||
RotateCounterClockwiseIcon,
|
||||
SwapIcon,
|
||||
} from '../../icons';
|
||||
import { cleanSet } from '../../util/clean-modify';
|
||||
import type { SourceImage } from '../../Compress';
|
||||
import { PreprocessorState } from 'client/lazy-app/feature-meta';
|
||||
import Cropper, { CropBox } from './Cropper';
|
||||
import CanvasImage from '../CanvasImage';
|
||||
import Expander from '../Options/Expander';
|
||||
import Select from '../Options/Select';
|
||||
import Checkbox from '../Options/Checkbox';
|
||||
|
||||
const ROTATE_ORIENTATIONS = [0, 90, 180, 270] as const;
|
||||
|
||||
const cropPresets = {
|
||||
square: {
|
||||
name: 'Square',
|
||||
ratio: 1,
|
||||
},
|
||||
'4:3': {
|
||||
name: '4:3',
|
||||
ratio: 4 / 3,
|
||||
},
|
||||
'16:9': {
|
||||
name: '16:9',
|
||||
ratio: 16 / 9,
|
||||
},
|
||||
'16:10': {
|
||||
name: '16:10',
|
||||
ratio: 16 / 10,
|
||||
},
|
||||
};
|
||||
|
||||
type CropPresetId = keyof typeof cropPresets;
|
||||
|
||||
interface Props {
|
||||
source: SourceImage;
|
||||
preprocessorState: PreprocessorState;
|
||||
mobileView: boolean;
|
||||
onCancel?(): void;
|
||||
onSave?(e: { preprocessorState: PreprocessorState }): void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
scale: number;
|
||||
editingScale: boolean;
|
||||
rotate: typeof ROTATE_ORIENTATIONS[number];
|
||||
// crop: false | CropBox;
|
||||
crop: CropBox;
|
||||
cropPreset: keyof typeof cropPresets | undefined;
|
||||
lockAspect: boolean;
|
||||
flip: PreprocessorState['flip'];
|
||||
}
|
||||
|
||||
const scaleToOpts: ScaleToOpts = {
|
||||
originX: '50%',
|
||||
originY: '50%',
|
||||
relativeTo: 'container',
|
||||
allowChangeEvent: true,
|
||||
};
|
||||
|
||||
export default class Transform extends Component<Props, State> {
|
||||
state: State = {
|
||||
scale: 1,
|
||||
editingScale: false,
|
||||
cropPreset: undefined,
|
||||
lockAspect: false,
|
||||
...this.fromPreprocessorState(this.props.preprocessorState),
|
||||
};
|
||||
pinchZoom = createRef<PinchZoom>();
|
||||
scaleInput = createRef<HTMLInputElement>();
|
||||
|
||||
// static getDerivedStateFromProps({ source, preprocessorState }: Props) {
|
||||
// return {
|
||||
// rotate: preprocessorState.rotate.rotate || 0,
|
||||
// crop: preprocessorState.crop || false,
|
||||
// flip: preprocessorState.flip || { horizontal: false, vertical: false },
|
||||
// };
|
||||
// }
|
||||
|
||||
componentWillReceiveProps(
|
||||
{ source, preprocessorState }: Props,
|
||||
{ crop, cropPreset }: State,
|
||||
) {
|
||||
if (preprocessorState !== this.props.preprocessorState) {
|
||||
this.setState(this.fromPreprocessorState(preprocessorState));
|
||||
}
|
||||
const { width, height } = source.decoded;
|
||||
const cropWidth = width - crop.left - crop.right;
|
||||
const cropHeight = height - crop.top - crop.bottom;
|
||||
for (const [id, preset] of Object.entries(cropPresets)) {
|
||||
if (cropHeight * preset.ratio === cropWidth) {
|
||||
if (cropPreset !== id) {
|
||||
this.setState({ cropPreset: id as CropPresetId });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fromPreprocessorState(preprocessorState?: PreprocessorState) {
|
||||
const state: Pick<State, 'rotate' | 'crop' | 'flip'> = {
|
||||
rotate: preprocessorState ? preprocessorState.rotate.rotate : 0,
|
||||
crop: Object.assign(
|
||||
{
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
(preprocessorState && preprocessorState.crop) || {},
|
||||
),
|
||||
flip: Object.assign(
|
||||
{
|
||||
horizontal: false,
|
||||
vertical: false,
|
||||
},
|
||||
(preprocessorState && preprocessorState.flip) || {},
|
||||
),
|
||||
};
|
||||
return state;
|
||||
}
|
||||
|
||||
private save = () => {
|
||||
const { preprocessorState, onSave } = this.props;
|
||||
const { rotate, crop, flip } = this.state;
|
||||
|
||||
let newState = cleanSet(preprocessorState, 'rotate.rotate', rotate);
|
||||
newState = cleanSet(newState, 'crop', crop);
|
||||
newState = cleanSet(newState, 'flip', flip);
|
||||
|
||||
console.log('u', JSON.parse(JSON.stringify(newState)));
|
||||
|
||||
if (onSave) onSave({ preprocessorState: newState });
|
||||
};
|
||||
|
||||
private cancel = () => {
|
||||
const { onCancel, onSave } = this.props;
|
||||
if (onCancel) onCancel();
|
||||
else if (onSave)
|
||||
onSave({ preprocessorState: this.props.preprocessorState });
|
||||
};
|
||||
|
||||
// private fitToViewport = () => {
|
||||
// const pinchZoom = this.pinchZoom.current;
|
||||
// const img = this.props.source?.preprocessed;
|
||||
// if (!img || !pinchZoom) return;
|
||||
// const scale = Number(Math.min(
|
||||
// (window.innerWidth - 20) / img.width,
|
||||
// (window.innerHeight - 20) / img.height
|
||||
// ).toFixed(2));
|
||||
// pinchZoom.scaleTo(Number(scale.toFixed(2)), { allowChangeEvent: true });
|
||||
// this.recenter();
|
||||
// };
|
||||
|
||||
// private recenter = () => {
|
||||
// const pinchZoom = this.pinchZoom.current;
|
||||
// const img = this.props.source?.preprocessed;
|
||||
// if (!img || !pinchZoom) return;
|
||||
// pinchZoom.setTransform({
|
||||
// x: (img.width - img.width * pinchZoom.scale) / 2,
|
||||
// y: (img.height - img.height * pinchZoom.scale) / 2,
|
||||
// allowChangeEvent: true
|
||||
// });
|
||||
// };
|
||||
|
||||
private zoomIn = () => {
|
||||
if (!this.pinchZoom.current) throw Error('Missing pinch-zoom element');
|
||||
this.pinchZoom.current.scaleTo(this.state.scale * 1.25, scaleToOpts);
|
||||
};
|
||||
|
||||
private zoomOut = () => {
|
||||
if (!this.pinchZoom.current) throw Error('Missing pinch-zoom element');
|
||||
this.pinchZoom.current.scaleTo(this.state.scale / 1.25, scaleToOpts);
|
||||
};
|
||||
|
||||
private onScaleValueFocus = () => {
|
||||
this.setState({ editingScale: true }, () => {
|
||||
if (this.scaleInput.current) {
|
||||
// Firefox unfocuses the input straight away unless I force a style
|
||||
// calculation here. I have no idea why, but it's late and I'm quite
|
||||
// tired.
|
||||
getComputedStyle(this.scaleInput.current).transform;
|
||||
this.scaleInput.current.focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
private onScaleInputBlur = () => {
|
||||
this.setState({ editingScale: false });
|
||||
};
|
||||
|
||||
private onScaleInputChanged = (event: Event) => {
|
||||
const target = event.target as HTMLInputElement;
|
||||
const percent = parseFloat(target.value);
|
||||
if (isNaN(percent)) return;
|
||||
if (!this.pinchZoom.current) throw Error('Missing pinch-zoom element');
|
||||
|
||||
this.pinchZoom.current.scaleTo(percent / 100, scaleToOpts);
|
||||
};
|
||||
|
||||
private onPinchZoomChange = () => {
|
||||
if (!this.pinchZoom.current) throw Error('Missing pinch-zoom element');
|
||||
this.setState({
|
||||
scale: this.pinchZoom.current.scale,
|
||||
});
|
||||
};
|
||||
|
||||
private onCropChange = (crop: CropBox) => {
|
||||
this.setState({ crop });
|
||||
};
|
||||
|
||||
private onCropPresetChange = (event: Event) => {
|
||||
const { value } = event.target as HTMLSelectElement;
|
||||
// @ts-ignore-next
|
||||
const cropPreset = cropPresets[value];
|
||||
this.setState({
|
||||
cropPreset,
|
||||
lockAspect: true,
|
||||
});
|
||||
};
|
||||
|
||||
private swapCropDimensions = () => {
|
||||
const { width, height } = this.props.source.decoded;
|
||||
let { left, right, top, bottom } = this.state.crop;
|
||||
const cropWidth = width - left - right;
|
||||
const cropHeight = height - top - bottom;
|
||||
const centerX = left - right;
|
||||
const centerY = top - bottom;
|
||||
const crop = {
|
||||
top: (width - cropWidth) / 2 + centerY / 2,
|
||||
bottom: (width - cropWidth) / 2 - centerY / 2,
|
||||
left: (height - cropHeight) / 2 + centerX / 2,
|
||||
right: (height - cropHeight) / 2 - centerX / 2,
|
||||
};
|
||||
this.setCrop(crop);
|
||||
};
|
||||
|
||||
private setCrop(crop: CropBox) {
|
||||
if (crop.top < 0) {
|
||||
crop.bottom += crop.top;
|
||||
crop.top = 0;
|
||||
}
|
||||
if (crop.bottom < 0) {
|
||||
crop.top += crop.bottom;
|
||||
crop.bottom = 0;
|
||||
}
|
||||
if (crop.left < 0) {
|
||||
crop.right += crop.left;
|
||||
crop.left = 0;
|
||||
}
|
||||
if (crop.right < 0) {
|
||||
crop.left += crop.right;
|
||||
crop.right = 0;
|
||||
}
|
||||
this.setState({ crop });
|
||||
}
|
||||
|
||||
// yeah these could just += 90
|
||||
private rotateClockwise = () => {
|
||||
let { rotate, crop } = this.state;
|
||||
this.setState({
|
||||
rotate: ((rotate + 90) % 360) as typeof ROTATE_ORIENTATIONS[number],
|
||||
crop: {
|
||||
top: crop.left,
|
||||
left: crop.bottom,
|
||||
bottom: crop.right,
|
||||
right: crop.top,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
private rotateCounterClockwise = () => {
|
||||
let { rotate, crop } = this.state;
|
||||
this.setState({
|
||||
rotate: (rotate
|
||||
? rotate - 90
|
||||
: 270) as typeof ROTATE_ORIENTATIONS[number],
|
||||
crop: {
|
||||
top: crop.right,
|
||||
right: crop.bottom,
|
||||
bottom: crop.left,
|
||||
left: crop.top,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
private flipHorizontally = () => {
|
||||
const { horizontal, vertical } = this.state.flip;
|
||||
this.setState({ flip: { horizontal: !horizontal, vertical } });
|
||||
};
|
||||
|
||||
private flipVertically = () => {
|
||||
const { horizontal, vertical } = this.state.flip;
|
||||
this.setState({ flip: { horizontal, vertical: !vertical } });
|
||||
};
|
||||
|
||||
// private update = (event: Event) => {
|
||||
// const { name, value } = event.target as HTMLInputElement;
|
||||
// const state = cleanSet(this.state, name, value);
|
||||
// this.setState(state);
|
||||
// };
|
||||
|
||||
private toggleLockAspect = () => {
|
||||
this.setState({ lockAspect: !this.state.lockAspect });
|
||||
};
|
||||
|
||||
private setCropWidth = (
|
||||
event: preact.JSX.TargetedEvent<HTMLInputElement, Event>,
|
||||
) => {
|
||||
const { width, height } = this.props.source.decoded;
|
||||
const newWidth = Math.min(width, parseInt(event.currentTarget.value, 10));
|
||||
let { top, right, bottom, left } = this.state.crop;
|
||||
const aspect = (width - left - right) / (height - top - bottom);
|
||||
right = width - newWidth - left;
|
||||
if (this.state.lockAspect) {
|
||||
const newHeight = newWidth / aspect;
|
||||
if (newHeight > height) return;
|
||||
bottom = height - newHeight - top;
|
||||
}
|
||||
this.setCrop({ top, right, bottom, left });
|
||||
};
|
||||
|
||||
private setCropHeight = (
|
||||
event: preact.JSX.TargetedEvent<HTMLInputElement, Event>,
|
||||
) => {
|
||||
const { width, height } = this.props.source.decoded;
|
||||
const newHeight = Math.min(height, parseInt(event.currentTarget.value, 10));
|
||||
let { top, right, bottom, left } = this.state.crop;
|
||||
const aspect = (width - left - right) / (height - top - bottom);
|
||||
bottom = height - newHeight - top;
|
||||
if (this.state.lockAspect) {
|
||||
const newWidth = newHeight * aspect;
|
||||
if (newWidth > width) return;
|
||||
right = width - newWidth - left;
|
||||
}
|
||||
this.setCrop({ top, right, bottom, left });
|
||||
};
|
||||
|
||||
// private onRotateClick = () => {
|
||||
// const { preprocessorState: inputProcessorState } = this.props;
|
||||
// if (!inputProcessorState) return;
|
||||
// const newState = cleanSet(
|
||||
// inputProcessorState,
|
||||
// 'rotate.rotate',
|
||||
// (inputProcessorState.rotate.rotate + 90) % 360,
|
||||
// );
|
||||
// this.props.onPreprocessorChange(newState);
|
||||
// };
|
||||
|
||||
render(
|
||||
{ mobileView, source }: Props,
|
||||
{ scale, editingScale, rotate, flip, crop, cropPreset, lockAspect }: State,
|
||||
) {
|
||||
const image = source.decoded;
|
||||
|
||||
const width = source.decoded.width - crop.left - crop.right;
|
||||
const height = source.decoded.height - crop.top - crop.bottom;
|
||||
|
||||
let transform =
|
||||
`rotate(${rotate}deg) ` +
|
||||
`scale(${flip.horizontal ? -1 : 1}, ${flip.vertical ? -1 : 1})`;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<CancelButton onClick={this.cancel} />
|
||||
<SaveButton onClick={this.save} />
|
||||
|
||||
<div class={style.transform}>
|
||||
<pinch-zoom
|
||||
class={style.pinchZoom}
|
||||
onChange={this.onPinchZoomChange}
|
||||
ref={this.pinchZoom}
|
||||
>
|
||||
{/* <Backdrop width={image.width} height={image.height} /> */}
|
||||
<div
|
||||
class={style.wrap}
|
||||
style={{
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
}}
|
||||
>
|
||||
<CanvasImage
|
||||
class={style.pinchTarget}
|
||||
image={image}
|
||||
style={{ transform }}
|
||||
/>
|
||||
{crop && (
|
||||
<Cropper
|
||||
size={{ width: image.width, height: image.height }}
|
||||
scale={scale}
|
||||
lockAspect={lockAspect}
|
||||
crop={crop}
|
||||
onChange={this.onCropChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</pinch-zoom>
|
||||
</div>
|
||||
|
||||
<div class={style.controls}>
|
||||
<div class={style.zoomControls}>
|
||||
<button class={style.button} onClick={this.zoomOut}>
|
||||
<RemoveIcon />
|
||||
</button>
|
||||
{editingScale ? (
|
||||
<input
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
max="1000000"
|
||||
ref={this.scaleInput}
|
||||
class={style.zoom}
|
||||
value={Math.round(scale * 100)}
|
||||
onInput={this.onScaleInputChanged}
|
||||
onBlur={this.onScaleInputBlur}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
class={style.zoom}
|
||||
tabIndex={0}
|
||||
onFocus={this.onScaleValueFocus}
|
||||
>
|
||||
<span class={style.zoomValue}>{Math.round(scale * 100)}</span>%
|
||||
</span>
|
||||
)}
|
||||
<button class={style.button} onClick={this.zoomIn}>
|
||||
<AddIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class={style.options}>
|
||||
<h3 class={style.optionsTitle}>Modify Source</h3>
|
||||
|
||||
<div class={style.optionsSection}>
|
||||
<h4 class={style.optionsSectionTitle}>Crop</h4>
|
||||
<div class={style.optionOneCell}>
|
||||
<Select
|
||||
large
|
||||
value={cropPreset}
|
||||
onChange={this.onCropPresetChange}
|
||||
>
|
||||
<option value="">Custom</option>
|
||||
{Object.entries(cropPresets).map(([type, preset]) => (
|
||||
<option value={type}>{preset.name}</option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
<label class={style.optionCheckbox}>
|
||||
<Checkbox checked={lockAspect} onClick={this.toggleLockAspect} />
|
||||
Lock aspect-ratio
|
||||
</label>
|
||||
<div class={style.optionsDimensions}>
|
||||
<input
|
||||
type="number"
|
||||
name="width"
|
||||
value={width}
|
||||
title="Crop width"
|
||||
onInput={this.setCropWidth}
|
||||
/>
|
||||
<button
|
||||
class={style.optionsButton}
|
||||
title="swap"
|
||||
onClick={this.swapCropDimensions}
|
||||
>
|
||||
<SwapIcon />
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
name="height"
|
||||
value={height}
|
||||
title="Crop height"
|
||||
onInput={this.setCropHeight}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class={style.optionButtonRow}>
|
||||
Flip
|
||||
<button
|
||||
class={style.optionsButton}
|
||||
data-active={flip.vertical}
|
||||
title="Flip vertically"
|
||||
onClick={this.flipVertically}
|
||||
>
|
||||
<FlipVerticallyIcon />
|
||||
</button>
|
||||
<button
|
||||
class={style.optionsButton}
|
||||
data-active={flip.horizontal}
|
||||
title="Flip horizontally"
|
||||
onClick={this.flipHorizontally}
|
||||
>
|
||||
<FlipHorizontallyIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class={style.optionButtonRow}>
|
||||
Rotate
|
||||
<button
|
||||
class={style.optionsButton}
|
||||
title="Rotate clockwise"
|
||||
onClick={this.rotateClockwise}
|
||||
>
|
||||
<RotateClockwiseIcon />
|
||||
</button>
|
||||
<button
|
||||
class={style.optionsButton}
|
||||
title="Rotate counter-clockwise"
|
||||
onClick={this.rotateCounterClockwise}
|
||||
>
|
||||
<RotateCounterClockwiseIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CancelButton = ({ onClick }: { onClick: () => void }) => (
|
||||
<button class={style.cancel} onClick={onClick}>
|
||||
<svg viewBox="0 0 80 80" width="80" height="80">
|
||||
<path d="M8.06 40.98c-.53-7.1 4.05-14.52 9.98-19.1s13.32-6.35 22.13-6.43c8.84-.12 19.12 1.51 24.4 7.97s5.6 17.74 1.68 26.97c-3.89 9.26-11.97 16.45-20.46 18-8.43 1.55-17.28-2.62-24.5-8.08S8.54 48.08 8.07 40.98z" />
|
||||
</svg>
|
||||
<CompareIcon class={style.icon} />
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
const SaveButton = ({ onClick }: { onClick: () => void }) => (
|
||||
<button class={style.save} onClick={onClick}>
|
||||
<svg viewBox="0 0 89 87" width="89" height="87">
|
||||
<path
|
||||
fill="#0c99ff"
|
||||
opacity=".7"
|
||||
d="M27.3 71.9c-8-4-15.6-12.3-16.9-21-1.2-8.7 4-17.8 10.5-26s14.4-15.6 24-16 21.2 6 28.6 16.5c7.4 10.5 10.8 25 6.6 34S64.1 71.7 54 73.5c-10.2 2-18.7 2.3-26.7-1.6z"
|
||||
/>
|
||||
<path
|
||||
fill="#0c99ff"
|
||||
opacity=".7"
|
||||
d="M14.6 24.8c4.3-7.8 13-15 21.8-15.7 8.7-.8 17.5 4.8 25.4 11.8 7.8 6.9 14.8 15.2 14.8 24.9s-7.2 20.7-18 27.6c-10.9 6.8-25.6 9.5-34.3 4.8S13 61.6 11.6 51.4c-1.3-10.3-1.3-18.8 3-26.6z"
|
||||
/>
|
||||
</svg>
|
||||
<CheckmarkIcon class={style.icon} />
|
||||
</button>
|
||||
);
|
||||
|
||||
interface BackdropProps {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
class Backdrop extends Component<BackdropProps> {
|
||||
shouldComponentUpdate({ width, height }: BackdropProps) {
|
||||
return width !== this.props.width || height !== this.props.height;
|
||||
}
|
||||
|
||||
/** @TODO this could at least use clip-path */
|
||||
render({ width, height }: BackdropProps) {
|
||||
const transform =
|
||||
`transform-origin: 50% 50%; transform: translate(var(--x), var(--y)) ` +
|
||||
`translate(-${width / 2}px, -${height / 2}px) ` +
|
||||
`scale(calc(var(--scale, 1) * 0.99999));`;
|
||||
return (
|
||||
<svg
|
||||
class={style.backdrop}
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
width="100%"
|
||||
height="100%"
|
||||
shape-rendering="optimizeSpeed"
|
||||
>
|
||||
<mask id="bgmask">
|
||||
<rect width="100%" height="100%" fill="white" />
|
||||
<rect
|
||||
style={transform}
|
||||
width={width}
|
||||
height={height}
|
||||
x="50%"
|
||||
y="50%"
|
||||
fill="black"
|
||||
/>
|
||||
</mask>
|
||||
<rect
|
||||
class={style.backdropArea}
|
||||
width="100%"
|
||||
height="100%"
|
||||
mask="url(#bgmask)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,351 +0,0 @@
|
||||
.transform {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
overflow: visible;
|
||||
|
||||
/*
|
||||
& > canvas {
|
||||
transition: transform 150ms ease;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform: none !important;
|
||||
will-change: initial !important;
|
||||
contain: strict;
|
||||
|
||||
& * {
|
||||
contain: strict;
|
||||
}
|
||||
/* background: rgba(255, 0, 0, 0.5); */
|
||||
}
|
||||
|
||||
.backdropArea {
|
||||
fill: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.pinch-zoom {
|
||||
composes: abs-fill from global;
|
||||
outline: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pinch-target {
|
||||
/* This fixes a severe painting bug in Chrome.
|
||||
* We should try to remove this once the issue is fixed.
|
||||
* https://bugs.chromium.org/p/chromium/issues/detail?id=870222#c10 */
|
||||
will-change: auto;
|
||||
/* Prevent the image becoming misshapen due to default flexbox layout. */
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cancel,
|
||||
.save {
|
||||
composes: unbutton from global;
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.save {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
|
||||
& > * {
|
||||
grid-area: 1/1/1/1;
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/* @TODO use grid */
|
||||
.cancel {
|
||||
fill: rgba(0, 0, 0, 0.4);
|
||||
|
||||
& > svg:not(.icon) {
|
||||
display: block;
|
||||
margin: -8px 0;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
& > .icon {
|
||||
position: absolute;
|
||||
left: 28px;
|
||||
top: 22px;
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
& > span {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
border-radius: 1rem;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
font-size: 80%;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
fill: rgba(0, 0, 0, 0.9);
|
||||
|
||||
& > span {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 78px;
|
||||
color: #fff;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-width: 250px;
|
||||
margin: 0;
|
||||
width: calc(100% - 60px);
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
align-self: end;
|
||||
border-radius: var(--options-radius) 0 0 var(--options-radius);
|
||||
animation: slideInFromRight 500ms ease-out forwards 1;
|
||||
--horizontal-padding: 15px;
|
||||
--main-theme-color: var(--blue);
|
||||
}
|
||||
@keyframes slideInFromRight {
|
||||
0% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.options-title {
|
||||
background-color: var(--main-theme-color);
|
||||
color: var(--dark-text);
|
||||
margin: 0;
|
||||
padding: 10px var(--horizontal-padding);
|
||||
font-weight: bold;
|
||||
font-size: 1.4rem;
|
||||
border-bottom: 1px solid var(--off-black);
|
||||
}
|
||||
|
||||
.options-section {
|
||||
padding: 5px 0;
|
||||
background: var(--off-black);
|
||||
}
|
||||
|
||||
.options-section-title {
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
padding: 5px var(--horizontal-padding);
|
||||
}
|
||||
|
||||
.option-base {
|
||||
display: grid;
|
||||
gap: 0.7em;
|
||||
align-items: center;
|
||||
padding: 5px var(--horizontal-padding);
|
||||
}
|
||||
|
||||
.options-button {
|
||||
composes: unbutton from global;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--dark-gray);
|
||||
color: var(--white);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--off-black);
|
||||
border-color: var(--med-gray);
|
||||
}
|
||||
|
||||
&[data-active] {
|
||||
background-color: var(--dark-gray);
|
||||
border-color: var(--med-gray);
|
||||
}
|
||||
}
|
||||
|
||||
.options-dimensions {
|
||||
composes: option-base;
|
||||
grid-template-columns: 1fr 0fr 1fr;
|
||||
|
||||
input {
|
||||
background: var(--white);
|
||||
color: var(--black);
|
||||
font: inherit;
|
||||
border: none;
|
||||
width: 100%;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.option-one-cell {
|
||||
composes: option-base;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.option-button-row {
|
||||
composes: option-base;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
}
|
||||
|
||||
.option-checkbox {
|
||||
composes: option-base;
|
||||
grid-template-columns: auto 1fr;
|
||||
}
|
||||
|
||||
/** Zoom controls */
|
||||
.controls {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 9px 84px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
/* Allow clicks to fall through to the pinch zoom area */
|
||||
pointer-events: none;
|
||||
|
||||
& > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 860px) {
|
||||
padding: 9px;
|
||||
top: auto;
|
||||
left: 320px;
|
||||
right: 320px;
|
||||
bottom: 0;
|
||||
flex-wrap: wrap-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-controls {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
|
||||
& > :not(:first-child) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
& > :not(:last-child) {
|
||||
margin-right: 0;
|
||||
border-right-width: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.button,
|
||||
.zoom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
margin: 4px;
|
||||
background-color: rgba(29, 29, 29, 0.92);
|
||||
border: 1px solid rgba(0, 0, 0, 0.67);
|
||||
border-radius: 6px;
|
||||
line-height: 1.1;
|
||||
white-space: nowrap;
|
||||
height: 39px;
|
||||
padding: 0 8px;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
|
||||
/*
|
||||
@media (min-width: 600px) {
|
||||
height: 39px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
*/
|
||||
|
||||
&:focus {
|
||||
/* box-shadow: 0 0 0 2px var(--hot-pink); */
|
||||
box-shadow: 0 0 0 2px #fff;
|
||||
outline: none;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
background: rgba(50, 50, 50, 0.92);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(72, 72, 72, 0.92);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom {
|
||||
cursor: text;
|
||||
width: 7rem;
|
||||
font: inherit;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
|
||||
&:focus {
|
||||
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2), 0 0 0 2px #fff;
|
||||
}
|
||||
}
|
||||
span.zoom {
|
||||
color: #939393;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 100;
|
||||
}
|
||||
input.zoom {
|
||||
font-size: 1.2rem;
|
||||
letter-spacing: 0.05rem;
|
||||
font-weight: 700;
|
||||
text-indent: 3px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.zoom-value {
|
||||
margin: 0 3px 0 0;
|
||||
padding: 0 2px;
|
||||
font-size: 1.2rem;
|
||||
letter-spacing: 0.05rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
border-bottom: 1px dashed #999;
|
||||
}
|
||||
|
||||
.buttons-no-wrap {
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
|
||||
& > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
@@ -30,9 +30,8 @@ import './custom-els/MultiPanel';
|
||||
import Results from './Results';
|
||||
import WorkerBridge from '../worker-bridge';
|
||||
import { resize } from 'features/processors/resize/client';
|
||||
import type SnackBarElement from 'shared/custom-els/snack-bar';
|
||||
import type SnackBarElement from 'shared/initial-app/custom-els/snack-bar';
|
||||
import { CopyAcrossIconProps, ExpandIcon } from '../icons';
|
||||
import Transform from './Transform';
|
||||
|
||||
export type OutputType = EncoderType | 'identity';
|
||||
|
||||
@@ -69,11 +68,8 @@ interface State {
|
||||
sides: [Side, Side];
|
||||
/** Source image load */
|
||||
loading: boolean;
|
||||
/** Showing preprocessor transformations modal */
|
||||
transform: boolean;
|
||||
error?: string;
|
||||
mobileView: boolean;
|
||||
altBackground: boolean;
|
||||
preprocessorState: PreprocessorState;
|
||||
encodedPreprocessorState?: PreprocessorState;
|
||||
}
|
||||
@@ -129,18 +125,13 @@ async function preprocessImage(
|
||||
): Promise<ImageData> {
|
||||
assertSignal(signal);
|
||||
let processedData = data;
|
||||
const { rotate, flip, crop } = preprocessorState;
|
||||
|
||||
if (rotate.rotate !== 0) {
|
||||
processedData = await workerBridge.rotate(signal, processedData, rotate);
|
||||
}
|
||||
|
||||
if (flip && (flip.horizontal || flip.vertical)) {
|
||||
processedData = await workerBridge.flip(signal, processedData, flip);
|
||||
}
|
||||
|
||||
if (crop && (crop.left || crop.top || crop.right || crop.bottom)) {
|
||||
processedData = await workerBridge.crop(signal, processedData, crop);
|
||||
if (preprocessorState.rotate.rotate !== 0) {
|
||||
processedData = await workerBridge.rotate(
|
||||
signal,
|
||||
processedData,
|
||||
preprocessorState.rotate,
|
||||
);
|
||||
}
|
||||
|
||||
return processedData;
|
||||
@@ -282,9 +273,6 @@ export default class Compress extends Component<Props, State> {
|
||||
state: State = {
|
||||
source: undefined,
|
||||
loading: false,
|
||||
/** @TODO remove this */
|
||||
// transform: true,
|
||||
transform: false,
|
||||
preprocessorState: defaultPreprocessorState,
|
||||
sides: [
|
||||
{
|
||||
@@ -306,7 +294,6 @@ export default class Compress extends Component<Props, State> {
|
||||
},
|
||||
],
|
||||
mobileView: this.widthQuery.matches,
|
||||
altBackground: false,
|
||||
};
|
||||
|
||||
private readonly encodeCache = new ResultCache();
|
||||
@@ -332,13 +319,7 @@ export default class Compress extends Component<Props, State> {
|
||||
this.setState({ mobileView: this.widthQuery.matches });
|
||||
};
|
||||
|
||||
private toggleBackground = () => {
|
||||
this.setState({
|
||||
altBackground: !this.state.altBackground,
|
||||
});
|
||||
};
|
||||
|
||||
private onEncoderTypeChange = (index: 0 | 1, newType: OutputType): void => {
|
||||
private onEncoderTypeChange(index: 0 | 1, newType: OutputType): void {
|
||||
this.setState({
|
||||
sides: cleanSet(
|
||||
this.state.sides,
|
||||
@@ -351,12 +332,12 @@ export default class Compress extends Component<Props, State> {
|
||||
},
|
||||
),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private onProcessorOptionsChange = (
|
||||
private onProcessorOptionsChange(
|
||||
index: 0 | 1,
|
||||
options: ProcessorState,
|
||||
): void => {
|
||||
): void {
|
||||
this.setState({
|
||||
sides: cleanSet(
|
||||
this.state.sides,
|
||||
@@ -364,12 +345,9 @@ export default class Compress extends Component<Props, State> {
|
||||
options,
|
||||
),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private onEncoderOptionsChange = (
|
||||
index: 0 | 1,
|
||||
options: EncoderOptions,
|
||||
): void => {
|
||||
private onEncoderOptionsChange(index: 0 | 1, options: EncoderOptions): void {
|
||||
this.setState({
|
||||
sides: cleanSet(
|
||||
this.state.sides,
|
||||
@@ -377,21 +355,7 @@ export default class Compress extends Component<Props, State> {
|
||||
options,
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
private showPreprocessorTransforms = () => {
|
||||
this.setState({ transform: true });
|
||||
};
|
||||
|
||||
private onTransformUpdated = ({
|
||||
preprocessorState,
|
||||
}: { preprocessorState?: PreprocessorState } = {}) => {
|
||||
console.log('onTransformUpdated', preprocessorState);
|
||||
if (preprocessorState) {
|
||||
this.onPreprocessorChange(preprocessorState);
|
||||
}
|
||||
this.setState({ transform: false });
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props): void {
|
||||
if (nextProps.file !== this.props.file) {
|
||||
@@ -823,31 +787,29 @@ export default class Compress extends Component<Props, State> {
|
||||
|
||||
render(
|
||||
{ onBack }: Props,
|
||||
{
|
||||
loading,
|
||||
sides,
|
||||
source,
|
||||
mobileView,
|
||||
altBackground,
|
||||
transform,
|
||||
preprocessorState,
|
||||
}: State,
|
||||
{ loading, sides, source, mobileView, preprocessorState }: State,
|
||||
) {
|
||||
const [leftSide, rightSide] = sides;
|
||||
const [leftImageData, rightImageData] = sides.map((i) => i.data);
|
||||
|
||||
transform = (source && source.decoded && transform) || false;
|
||||
|
||||
const options = sides.map((side, index) => (
|
||||
<Options
|
||||
index={index as 0 | 1}
|
||||
source={source}
|
||||
mobileView={mobileView}
|
||||
processorState={side.latestSettings.processorState}
|
||||
encoderState={side.latestSettings.encoderState}
|
||||
onEncoderTypeChange={this.onEncoderTypeChange}
|
||||
onEncoderOptionsChange={this.onEncoderOptionsChange}
|
||||
onProcessorOptionsChange={this.onProcessorOptionsChange}
|
||||
onEncoderTypeChange={this.onEncoderTypeChange.bind(
|
||||
this,
|
||||
index as 0 | 1,
|
||||
)}
|
||||
onEncoderOptionsChange={this.onEncoderOptionsChange.bind(
|
||||
this,
|
||||
index as 0 | 1,
|
||||
)}
|
||||
onProcessorOptionsChange={this.onProcessorOptionsChange.bind(
|
||||
this,
|
||||
index as 0 | 1,
|
||||
)}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -892,68 +854,39 @@ export default class Compress extends Component<Props, State> {
|
||||
rightDisplaySettings.processorState.resize.fitMethod === 'contain';
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`${style.compress} ${transform ? style.transforming : ''} ${
|
||||
altBackground ? style.altBackground : ''
|
||||
}`}
|
||||
>
|
||||
<div class={style.compress}>
|
||||
<Output
|
||||
hidden={transform}
|
||||
source={source}
|
||||
mobileView={mobileView}
|
||||
leftCompressed={leftImageData}
|
||||
rightCompressed={rightImageData}
|
||||
leftImgContain={leftImgContain}
|
||||
rightImgContain={rightImgContain}
|
||||
onBack={onBack}
|
||||
preprocessorState={preprocessorState}
|
||||
onPreprocessorChange={this.onPreprocessorChange}
|
||||
onShowPreprocessorTransforms={this.showPreprocessorTransforms}
|
||||
onToggleBackground={this.toggleBackground}
|
||||
/>
|
||||
<button class={style.back} onClick={onBack}>
|
||||
<svg viewBox="0 0 61 53.3">
|
||||
<title>Back</title>
|
||||
<path
|
||||
class={style.backBlob}
|
||||
d="M0 25.6c-.5-7.1 4.1-14.5 10-19.1S23.4.1 32.2 0c8.8 0 19 1.6 24.4 8s5.6 17.8 1.7 27a29.7 29.7 0 01-20.5 18c-8.4 1.5-17.3-2.6-24.5-8S.5 32.6.1 25.6z"
|
||||
/>
|
||||
<path
|
||||
class={style.backX}
|
||||
d="M41.6 17.1l-2-2.1-8.3 8.2-8.2-8.2-2 2 8.2 8.3-8.3 8.2 2.1 2 8.2-8.1 8.3 8.2 2-2-8.2-8.3z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{mobileView ? (
|
||||
<div class={style.options}>
|
||||
<multi-panel class={style.multiPanel} open-one-only>
|
||||
{results[0]}
|
||||
<div class={style.options1Theme}>{options[0]}</div>
|
||||
{options[0]}
|
||||
{results[1]}
|
||||
<div class={style.options2Theme}>{options[1]}</div>
|
||||
{options[1]}
|
||||
</multi-panel>
|
||||
</div>
|
||||
) : (
|
||||
[
|
||||
<div class={style.options1} key="options1">
|
||||
<div class={style.options} key="options0">
|
||||
{options[0]}
|
||||
{results[0]}
|
||||
</div>,
|
||||
<div class={style.options2} key="options2">
|
||||
<div class={style.options} key="options1">
|
||||
{options[1]}
|
||||
{results[1]}
|
||||
</div>,
|
||||
]
|
||||
)}
|
||||
|
||||
{transform && (
|
||||
<Transform
|
||||
mobileView={mobileView}
|
||||
source={source!}
|
||||
preprocessorState={preprocessorState!}
|
||||
onSave={this.onTransformUpdated}
|
||||
onCancel={this.onTransformUpdated}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,110 +3,39 @@
|
||||
height: 100%;
|
||||
contain: strict;
|
||||
display: grid;
|
||||
grid-template-rows: max-content 1fr;
|
||||
grid-template-areas:
|
||||
'header'
|
||||
'opts';
|
||||
|
||||
--options-radius: 7px;
|
||||
align-items: end;
|
||||
align-content: end;
|
||||
grid-template-rows: 1fr auto;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
grid-template-rows: max-content 1fr;
|
||||
grid-template-columns: max-content 1fr max-content;
|
||||
grid-template-areas:
|
||||
'header header header'
|
||||
'optsLeft viewportOpts optsRight';
|
||||
}
|
||||
|
||||
/* darker squares background */
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #000;
|
||||
opacity: 0.8;
|
||||
transition: opacity 500ms ease;
|
||||
}
|
||||
|
||||
&.alt-background::before {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* transformation is modal and we sweep away the comparison UI */
|
||||
&.transforming {
|
||||
& > .options {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
& > .options + .options {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
& > .back {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > :first-child {
|
||||
display: none;
|
||||
}
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-rows: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
position: relative;
|
||||
color: #fff;
|
||||
opacity: 0.9;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
width: calc(100% - 60px);
|
||||
max-height: 100%;
|
||||
max-height: calc(100% - 104px);
|
||||
overflow: hidden;
|
||||
align-self: end;
|
||||
grid-area: opts;
|
||||
transition: transform 500ms ease;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
max-height: calc(100% - 75px);
|
||||
width: 300px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.options-1-theme {
|
||||
--main-theme-color: var(--pink);
|
||||
--header-text-color: var(--white);
|
||||
--scroller-radius: var(--options-radius) var(--options-radius) 0 0;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
--scroller-radius: 0 var(--options-radius) var(--options-radius) 0;
|
||||
@media (min-width: 860px) {
|
||||
max-height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
.options-2-theme {
|
||||
--main-theme-color: var(--blue);
|
||||
--header-text-color: var(--dark-text);
|
||||
--scroller-radius: var(--options-radius) var(--options-radius) 0 0;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
--scroller-radius: var(--options-radius) 0 0 var(--options-radius);
|
||||
}
|
||||
}
|
||||
|
||||
.options-1 {
|
||||
composes: options;
|
||||
composes: options-1-theme;
|
||||
grid-area: optsLeft;
|
||||
}
|
||||
|
||||
.options-2 {
|
||||
composes: options;
|
||||
composes: options-2-theme;
|
||||
grid-area: optsRight;
|
||||
}
|
||||
|
||||
.multi-panel {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -144,33 +73,3 @@
|
||||
:focus .expand-icon {
|
||||
fill: #34b9eb;
|
||||
}
|
||||
|
||||
.back {
|
||||
composes: unbutton from global;
|
||||
position: relative;
|
||||
grid-area: header;
|
||||
margin: 9px;
|
||||
justify-self: start;
|
||||
align-self: start;
|
||||
|
||||
& > svg {
|
||||
width: 47px;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
margin: 14px;
|
||||
|
||||
& > svg {
|
||||
width: 58px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.back-blob {
|
||||
fill: var(--hot-pink);
|
||||
opacity: 0.77;
|
||||
}
|
||||
|
||||
.back-x {
|
||||
fill: var(--white);
|
||||
}
|
||||
|
||||
@@ -11,50 +11,6 @@ const Icon = (props: preact.JSX.HTMLAttributes) => (
|
||||
/>
|
||||
);
|
||||
|
||||
export const SwapIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M9.01 14H2v2h7.01v3L13 15l-3.99-4zm5.98-1v-3H22V8h-7.01V5L11 9z" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const FlipVerticallyIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M21 9V7h-2v2zM9 5V3H7v2zM5 21h14a2 2 0 002-2v-4h-2v4H5v-4H3v4a2 2 0 002 2zM3 5h2V3a2 2 0 00-2 2zm20 8v-2H1v2zm-6-8V3h-2v2zM5 9V7H3v2zm8-4V3h-2v2zm8 0a2 2 0 00-2-2v2z" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const FlipHorizontallyIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M15 21h2v-2h-2zm4-12h2V7h-2zM3 5v14a2 2 0 002 2h4v-2H5V5h4V3H5a2 2 0 00-2 2zm16-2v2h2a2 2 0 00-2-2zm-8 20h2V1h-2zm8-6h2v-2h-2zM15 5h2V3h-2zm4 8h2v-2h-2zm0 8a2 2 0 002-2h-2z" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const RotateClockwiseIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M16.05 5.34l-5.2-5.2v3.5a9.12 9.12 0 000 18.1v-2.3a6.84 6.84 0 010-13.5v4.47zm5 6.22a9.03 9.03 0 00-1.85-4.44l-1.62 1.62a6.63 6.63 0 011.16 2.82zm-7.91 7.87v2.31a9.05 9.05 0 004.45-1.84l-1.64-1.64a6.6 6.6 0 01-2.81 1.18zm4.44-2.76l1.62 1.61a9.03 9.03 0 001.85-4.44h-2.3a6.73 6.73 0 01-1.17 2.83z" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const RotateCounterClockwiseIcon = (
|
||||
props: preact.JSX.HTMLAttributes,
|
||||
) => (
|
||||
<Icon {...props}>
|
||||
<path d="M7.95 5.34l5.19-5.2v3.5a9.12 9.12 0 010 18.1v-2.3a6.84 6.84 0 000-13.5v4.47zm-5 6.22A9.03 9.03 0 014.8 7.12l1.62 1.62a6.63 6.63 0 00-1.17 2.82zm7.9 7.87v2.31A9.05 9.05 0 016.4 19.9l1.65-1.64a6.6 6.6 0 002.8 1.17zm-4.43-2.76L4.8 18.28a9.03 9.03 0 01-1.85-4.44h2.3a6.73 6.73 0 001.17 2.83z" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const CheckmarkIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M9.76 17.56l-4.55-4.55-1.52 1.52 6.07 6.08 13-13.02-1.51-1.52z" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const CompareIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M9.77 1.94h-5.6a2.24 2.24 0 00-2.22 2.25v15.65a2.24 2.24 0 002.24 2.23h5.59v2.24h2.23V-.31H9.78zm0 16.77h-5.6l5.6-6.7zM19.83 1.94h-5.6v2.25h5.6v14.53l-5.6-6.7v10.05h5.6a2.24 2.24 0 002.23-2.23V4.18a2.24 2.24 0 00-2.23-2.24z" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const DownloadIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M19 12v7H5v-7H3v7c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-7h-2zm-6 .7l2.6-2.6 1.4 1.4-5 5-5-5 1.4-1.4 2.6 2.6V3h2z" />
|
||||
@@ -81,14 +37,6 @@ export const RotateIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const MoreIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<circle cx="12" cy="6" r="2" fill="#fff" />
|
||||
<circle cx="12" cy="12" r="2" fill="#fff" />
|
||||
<circle cx="12" cy="18" r="2" fill="#fff" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const AddIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
||||
@@ -119,10 +67,10 @@ export const ExpandIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const Arrow = () => (
|
||||
<svg viewBox="0 -1.95 9.8 9.8">
|
||||
<path d="M8.2.2a1 1 0 011.4 1.4l-4 4a1 1 0 01-1.4 0l-4-4A1 1 0 011.6.2l3.3 3.3L8.2.2z" />
|
||||
</svg>
|
||||
export const BackIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M20 11H7.8l5.6-5.6L12 4l-8 8 8 8 1.4-1.4L7.8 13H20v-2z" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
const copyAcrossRotations = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type SnackBarElement from 'shared/custom-els/snack-bar';
|
||||
import type SnackBarElement from 'shared/initial-app/custom-els/snack-bar';
|
||||
|
||||
import { get, set } from 'idb-keyval';
|
||||
|
||||
|
||||
22
src/client/missing-types.d.ts
vendored
@@ -11,7 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
/// <reference path="../../missing-types.d.ts" />
|
||||
/// <reference path="../shared/prerendered-app/Intro/missing-types.d.ts" />
|
||||
/// <reference path="../shared/initial-app/Intro/missing-types.d.ts" />
|
||||
|
||||
interface Navigator {
|
||||
readonly standalone: boolean;
|
||||
@@ -25,3 +25,23 @@ declare module 'service-worker:*' {
|
||||
}
|
||||
|
||||
declare module 'preact/debug' {}
|
||||
|
||||
interface ResizeObserverCallback {
|
||||
(entries: ResizeObserverEntry[], observer: ResizeObserver): void;
|
||||
}
|
||||
|
||||
interface ResizeObserverEntry {
|
||||
readonly target: Element;
|
||||
readonly contentRect: DOMRectReadOnly;
|
||||
}
|
||||
|
||||
interface ResizeObserver {
|
||||
observe(target: Element): void;
|
||||
unobserve(target: Element): void;
|
||||
disconnect(): void;
|
||||
}
|
||||
|
||||
declare var ResizeObserver: {
|
||||
prototype: ResizeObserver;
|
||||
new (callback: ResizeObserverCallback): ResizeObserver;
|
||||
};
|
||||
|
||||
14
src/copy/_headers
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Cache-Control: no-cache
|
||||
|
||||
/c/*
|
||||
Cache-Control: max-age=31536000
|
||||
|
||||
# COOP+COEP for WebAssembly threads.
|
||||
/*
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
# Origin trial for WebAssembly SIMD.
|
||||
# ATTENTION: This one is configured for dev--squoosh.netlify.app preview.
|
||||
# For production squoosh.app, replace with AgoKiDqjr0GVPtrwV/vuVlrrSvbDa5Yb99s+q66ly816DrrAQ8Cdas33NgDtmhxM4BtDP9PEdyuxHPyTQHD5ZAcAAABUeyJvcmlnaW4iOiJodHRwczovL3NxdW9vc2guYXBwOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseVNpbWQiLCJleHBpcnkiOjE2MDg2NzI5OTR9.
|
||||
Origin-Trial: As3b1fXjclhF8ZgvUkIqOo3r1/Jqvx0mNuT6Ilgb7SdpeJnV8lUdYr7i+OKgCmcVTWkqjkF23LJ+xZ111VYMEQIAAABheyJvcmlnaW4iOiJodHRwczovL2Rldi0tc3F1b29zaC5uZXRsaWZ5LmFwcDo0NDMiLCJmZWF0dXJlIjoiV2ViQXNzZW1ibHlTaW1kIiwiZXhwaXJ5IjoxNjA5NDI4Nzk4fQ==
|
||||
@@ -1 +1,2 @@
|
||||
/editor / 301
|
||||
/index.html / 301
|
||||
/* /index.html 301
|
||||
@@ -8,7 +8,6 @@ import Expander from 'client/lazy-app/Compress/Options/Expander';
|
||||
import Select from 'client/lazy-app/Compress/Options/Select';
|
||||
import Range from 'client/lazy-app/Compress/Options/Range';
|
||||
import linkState from 'linkstate';
|
||||
import Revealer from 'client/lazy-app/Compress/Options/Revealer';
|
||||
|
||||
export const encode = (
|
||||
signal: AbortSignal,
|
||||
@@ -210,12 +209,12 @@ export class Options extends Component<Props, State> {
|
||||
) {
|
||||
return (
|
||||
<form class={style.optionsSection} onSubmit={preventDefault}>
|
||||
<label class={style.optionToggle}>
|
||||
Lossless
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={lossless}
|
||||
onChange={this._inputChange('lossless', 'boolean')}
|
||||
/>
|
||||
Lossless
|
||||
</label>
|
||||
<Expander>
|
||||
{!lossless && (
|
||||
@@ -243,22 +242,22 @@ export class Options extends Component<Props, State> {
|
||||
</div>
|
||||
)}
|
||||
</Expander>
|
||||
<label class={style.optionToggle}>
|
||||
Separate alpha quality
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={separateAlpha}
|
||||
onChange={this._inputChange('separateAlpha', 'boolean')}
|
||||
/>
|
||||
Separate alpha quality
|
||||
</label>
|
||||
<Expander>
|
||||
{separateAlpha && (
|
||||
<div>
|
||||
<label class={style.optionToggle}>
|
||||
Lossless alpha
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={losslessAlpha}
|
||||
onChange={this._inputChange('losslessAlpha', 'boolean')}
|
||||
/>
|
||||
Lossless alpha
|
||||
</label>
|
||||
<Expander>
|
||||
{!losslessAlpha && (
|
||||
@@ -289,23 +288,23 @@ export class Options extends Component<Props, State> {
|
||||
</div>
|
||||
)}
|
||||
</Expander>
|
||||
<label class={style.optionReveal}>
|
||||
<Revealer
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={showAdvanced}
|
||||
onChange={linkState(this, 'showAdvanced')}
|
||||
/>
|
||||
Advanced settings
|
||||
Show advanced settings
|
||||
</label>
|
||||
<Expander>
|
||||
{showAdvanced && (
|
||||
<div>
|
||||
{/*<label class={style.optionToggle}>
|
||||
Grayscale
|
||||
{/*<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
data-set-state="grayscale"
|
||||
checked={grayscale}
|
||||
onChange={this._inputChange('grayscale', 'boolean')}
|
||||
/>
|
||||
Grayscale
|
||||
</label>*/}
|
||||
<Expander>
|
||||
{!grayscale && !lossless && (
|
||||
|
||||
@@ -123,23 +123,23 @@ export class Options extends Component<Props, State> {
|
||||
// gathering the data.
|
||||
return (
|
||||
<form class={style.optionsSection} onSubmit={preventDefault}>
|
||||
<label class={style.optionToggle}>
|
||||
Lossless
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="lossless"
|
||||
checked={lossless}
|
||||
onChange={this._inputChange('lossless', 'boolean')}
|
||||
/>
|
||||
Lossless
|
||||
</label>
|
||||
<Expander>
|
||||
{lossless && (
|
||||
<label class={style.optionToggle}>
|
||||
Slight loss
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="slightLoss"
|
||||
checked={slightLoss}
|
||||
onChange={this._inputChange('slightLoss', 'boolean')}
|
||||
/>
|
||||
Slight loss
|
||||
</label>
|
||||
)}
|
||||
</Expander>
|
||||
@@ -157,8 +157,7 @@ export class Options extends Component<Props, State> {
|
||||
Quality:
|
||||
</Range>
|
||||
</div>
|
||||
<label class={style.optionToggle}>
|
||||
Auto edge filter
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="autoEdgeFilter"
|
||||
checked={autoEdgePreservingFilter}
|
||||
@@ -167,6 +166,7 @@ export class Options extends Component<Props, State> {
|
||||
'boolean',
|
||||
)}
|
||||
/>
|
||||
Auto edge filter
|
||||
</label>
|
||||
<Expander>
|
||||
{!autoEdgePreservingFilter && (
|
||||
@@ -188,13 +188,13 @@ export class Options extends Component<Props, State> {
|
||||
</div>
|
||||
)}
|
||||
</Expander>
|
||||
<label class={style.optionToggle}>
|
||||
Progressive rendering
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="progressive"
|
||||
checked={progressive}
|
||||
onChange={this._inputChange('progressive', 'boolean')}
|
||||
/>
|
||||
Progressive rendering
|
||||
</label>
|
||||
<div class={style.optionOneCell}>
|
||||
<Range
|
||||
|
||||
@@ -10,56 +10,24 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import type { JXLModule } from 'codecs/jxl/enc/jxl_enc';
|
||||
import jxlEncoder, { JXLModule } from 'codecs/jxl/enc/jxl_enc';
|
||||
import wasmUrl from 'url:codecs/jxl/enc/jxl_enc.wasm';
|
||||
import { initEmscriptenModule } from 'features/worker-utils';
|
||||
import type { EncodeOptions } from '../shared/meta';
|
||||
|
||||
import { initEmscriptenModule } from 'features/worker-utils';
|
||||
import { threads, simd } from 'wasm-feature-detect';
|
||||
|
||||
import wasmUrl from 'url:codecs/jxl/enc/jxl_enc.wasm';
|
||||
|
||||
import wasmUrlWithMT from 'url:codecs/jxl/enc/jxl_enc_mt.wasm';
|
||||
import workerUrl from 'omt:codecs/jxl/enc/jxl_enc_mt.worker.js';
|
||||
|
||||
import wasmUrlWithMTAndSIMD from 'url:codecs/jxl/enc/jxl_enc_mt_simd.wasm';
|
||||
import workerUrlWithSIMD from 'omt:codecs/jxl/enc/jxl_enc_mt_simd.worker.js';
|
||||
|
||||
let emscriptenModule: Promise<JXLModule>;
|
||||
|
||||
async function init() {
|
||||
if (await threads()) {
|
||||
if (await simd()) {
|
||||
const jxlEncoder = await import('codecs/jxl/enc/jxl_enc_mt_simd');
|
||||
return initEmscriptenModule(
|
||||
jxlEncoder.default,
|
||||
wasmUrlWithMTAndSIMD,
|
||||
workerUrlWithSIMD,
|
||||
);
|
||||
}
|
||||
const jxlEncoder = await import('codecs/jxl/enc/jxl_enc_mt');
|
||||
return initEmscriptenModule(
|
||||
jxlEncoder.default,
|
||||
wasmUrlWithMT,
|
||||
workerUrl,
|
||||
);
|
||||
}
|
||||
const jxlEncoder = await import('codecs/jxl/enc/jxl_enc');
|
||||
return initEmscriptenModule(
|
||||
jxlEncoder.default,
|
||||
wasmUrl,
|
||||
);
|
||||
}
|
||||
|
||||
export default async function encode(
|
||||
data: ImageData,
|
||||
options: EncodeOptions,
|
||||
): Promise<ArrayBuffer> {
|
||||
if (!emscriptenModule) emscriptenModule = init();
|
||||
if (!emscriptenModule) {
|
||||
emscriptenModule = initEmscriptenModule(jxlEncoder, wasmUrl);
|
||||
}
|
||||
|
||||
const module = await emscriptenModule;
|
||||
const result = module.encode(data.data, data.width, data.height, options);
|
||||
|
||||
if (!result) throw new Error('Encoding error.');
|
||||
|
||||
return result.buffer;
|
||||
// wasm can’t run on SharedArrayBuffers, so we hard-cast to ArrayBuffer.
|
||||
return result.buffer as ArrayBuffer;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import Range from 'client/lazy-app/Compress/Options/Range';
|
||||
import Checkbox from 'client/lazy-app/Compress/Options/Checkbox';
|
||||
import Expander from 'client/lazy-app/Compress/Options/Expander';
|
||||
import Select from 'client/lazy-app/Compress/Options/Select';
|
||||
import Revealer from 'client/lazy-app/Compress/Options/Revealer';
|
||||
|
||||
export function encode(
|
||||
signal: AbortSignal,
|
||||
@@ -117,12 +116,12 @@ export class Options extends Component<Props, State> {
|
||||
Quality:
|
||||
</Range>
|
||||
</div>
|
||||
<label class={style.optionReveal}>
|
||||
<Revealer
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={showAdvanced}
|
||||
onChange={linkState(this, 'showAdvanced')}
|
||||
/>
|
||||
Advanced settings
|
||||
Show advanced settings
|
||||
</label>
|
||||
<Expander>
|
||||
{showAdvanced ? (
|
||||
@@ -142,13 +141,13 @@ export class Options extends Component<Props, State> {
|
||||
<Expander>
|
||||
{options.color_space === MozJpegColorSpace.YCbCr ? (
|
||||
<div>
|
||||
<label class={style.optionToggle}>
|
||||
Auto subsample chroma
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="auto_subsample"
|
||||
checked={options.auto_subsample}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Auto subsample chroma
|
||||
</label>
|
||||
<Expander>
|
||||
{options.auto_subsample ? null : (
|
||||
@@ -165,13 +164,13 @@ export class Options extends Component<Props, State> {
|
||||
</div>
|
||||
)}
|
||||
</Expander>
|
||||
<label class={style.optionToggle}>
|
||||
Separate chroma quality
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="separate_chroma_quality"
|
||||
checked={options.separate_chroma_quality}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Separate chroma quality
|
||||
</label>
|
||||
<Expander>
|
||||
{options.separate_chroma_quality ? (
|
||||
@@ -191,35 +190,35 @@ export class Options extends Component<Props, State> {
|
||||
</div>
|
||||
) : null}
|
||||
</Expander>
|
||||
<label class={style.optionToggle}>
|
||||
Pointless spec compliance
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="baseline"
|
||||
checked={options.baseline}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Pointless spec compliance
|
||||
</label>
|
||||
<Expander>
|
||||
{options.baseline ? null : (
|
||||
<label class={style.optionToggle}>
|
||||
Progressive rendering
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="progressive"
|
||||
checked={options.progressive}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Progressive rendering
|
||||
</label>
|
||||
)}
|
||||
</Expander>
|
||||
<Expander>
|
||||
{options.baseline ? (
|
||||
<label class={style.optionToggle}>
|
||||
Optimize Huffman table
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="optimize_coding"
|
||||
checked={options.optimize_coding}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Optimize Huffman table
|
||||
</label>
|
||||
) : null}
|
||||
</Expander>
|
||||
@@ -252,33 +251,33 @@ export class Options extends Component<Props, State> {
|
||||
<option value="8">Peterson et al</option>
|
||||
</Select>
|
||||
</label>
|
||||
<label class={style.optionToggle}>
|
||||
Trellis multipass
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="trellis_multipass"
|
||||
checked={options.trellis_multipass}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Trellis multipass
|
||||
</label>
|
||||
<Expander>
|
||||
{options.trellis_multipass ? (
|
||||
<label class={style.optionToggle}>
|
||||
Optimize zero block runs
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="trellis_opt_zero"
|
||||
checked={options.trellis_opt_zero}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Optimize zero block runs
|
||||
</label>
|
||||
) : null}
|
||||
</Expander>
|
||||
<label class={style.optionToggle}>
|
||||
Optimize after trellis quantization
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="trellis_opt_table"
|
||||
checked={options.trellis_opt_table}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Optimize after trellis quantization
|
||||
</label>
|
||||
<div class={style.optionOneCell}>
|
||||
<Range
|
||||
|
||||
@@ -12,7 +12,6 @@ import Range from 'client/lazy-app/Compress/Options/Range';
|
||||
import Checkbox from 'client/lazy-app/Compress/Options/Checkbox';
|
||||
import Expander from 'client/lazy-app/Compress/Options/Expander';
|
||||
import Select from 'client/lazy-app/Compress/Options/Select';
|
||||
import Revealer from 'client/lazy-app/Compress/Options/Revealer';
|
||||
|
||||
export const encode = (
|
||||
signal: AbortSignal,
|
||||
@@ -180,8 +179,7 @@ export class Options extends Component<Props, State> {
|
||||
Slight loss:
|
||||
</Range>
|
||||
</div>
|
||||
<label class={style.optionToggle}>
|
||||
Discrete tone image
|
||||
<label class={style.optionInputFirst}>
|
||||
{/*
|
||||
Although there are 3 different kinds of image hint, webp only
|
||||
seems to do something with the 'graph' type, and I don't really
|
||||
@@ -192,6 +190,7 @@ export class Options extends Component<Props, State> {
|
||||
checked={options.image_hint === WebPImageHint.WEBP_HINT_GRAPH}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Discrete tone image
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
@@ -225,23 +224,23 @@ export class Options extends Component<Props, State> {
|
||||
Quality:
|
||||
</Range>
|
||||
</div>
|
||||
<label class={style.optionReveal}>
|
||||
<Revealer
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={showAdvanced}
|
||||
onChange={linkState(this, 'showAdvanced')}
|
||||
/>
|
||||
Advanced settings
|
||||
Show advanced settings
|
||||
</label>
|
||||
<Expander>
|
||||
{showAdvanced ? (
|
||||
<div>
|
||||
<label class={style.optionToggle}>
|
||||
Compress alpha
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="alpha_compression"
|
||||
checked={!!options.alpha_compression}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Compress alpha
|
||||
</label>
|
||||
<div class={style.optionOneCell}>
|
||||
<Range
|
||||
@@ -265,13 +264,13 @@ export class Options extends Component<Props, State> {
|
||||
Alpha filter quality:
|
||||
</Range>
|
||||
</div>
|
||||
<label class={style.optionToggle}>
|
||||
Auto adjust filter strength
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="autofilter"
|
||||
checked={!!options.autofilter}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Auto adjust filter strength
|
||||
</label>
|
||||
<Expander>
|
||||
{options.autofilter ? null : (
|
||||
@@ -288,13 +287,13 @@ export class Options extends Component<Props, State> {
|
||||
</div>
|
||||
)}
|
||||
</Expander>
|
||||
<label class={style.optionToggle}>
|
||||
Strong filter
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="filter_type"
|
||||
checked={!!options.filter_type}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Strong filter
|
||||
</label>
|
||||
<div class={style.optionOneCell}>
|
||||
<Range
|
||||
@@ -307,13 +306,13 @@ export class Options extends Component<Props, State> {
|
||||
Filter sharpness:
|
||||
</Range>
|
||||
</div>
|
||||
<label class={style.optionToggle}>
|
||||
Sharp RGB→YUV conversion
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="use_sharp_yuv"
|
||||
checked={!!options.use_sharp_yuv}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Sharp RGB→YUV conversion
|
||||
</label>
|
||||
<div class={style.optionOneCell}>
|
||||
<Range
|
||||
@@ -383,24 +382,24 @@ export class Options extends Component<Props, State> {
|
||||
// gathering the data.
|
||||
return (
|
||||
<form class={style.optionsSection} onSubmit={preventDefault}>
|
||||
<label class={style.optionToggle}>
|
||||
Lossless
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="lossless"
|
||||
checked={!!options.lossless}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Lossless
|
||||
</label>
|
||||
{options.lossless
|
||||
? this._losslessSpecificOptions(options)
|
||||
: this._lossySpecificOptions(options)}
|
||||
<label class={style.optionToggle}>
|
||||
Preserve transparent data
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="exact"
|
||||
checked={!!options.exact}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Preserve transparent data
|
||||
</label>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@ import Select from 'client/lazy-app/Compress/Options/Select';
|
||||
import Checkbox from 'client/lazy-app/Compress/Options/Checkbox';
|
||||
import Expander from 'client/lazy-app/Compress/Options/Expander';
|
||||
import linkState from 'linkstate';
|
||||
import Revealer from 'client/lazy-app/Compress/Options/Revealer';
|
||||
|
||||
export const encode = (
|
||||
signal: AbortSignal,
|
||||
@@ -155,12 +154,12 @@ export class Options extends Component<Props, State> {
|
||||
) {
|
||||
return (
|
||||
<form class={style.optionsSection} onSubmit={preventDefault}>
|
||||
<label class={style.optionToggle}>
|
||||
Lossless
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={lossless}
|
||||
onChange={this._inputChange('lossless', 'boolean')}
|
||||
/>
|
||||
Lossless
|
||||
</label>
|
||||
<Expander>
|
||||
{lossless && (
|
||||
@@ -191,12 +190,12 @@ export class Options extends Component<Props, State> {
|
||||
Quality:
|
||||
</Range>
|
||||
</div>
|
||||
<label class={style.optionToggle}>
|
||||
Separate alpha quality
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={separateAlpha}
|
||||
onChange={this._inputChange('separateAlpha', 'boolean')}
|
||||
/>
|
||||
Separate alpha quality
|
||||
</label>
|
||||
<Expander>
|
||||
{separateAlpha && (
|
||||
@@ -213,12 +212,12 @@ export class Options extends Component<Props, State> {
|
||||
</div>
|
||||
)}
|
||||
</Expander>
|
||||
<label class={style.optionReveal}>
|
||||
<Revealer
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={showAdvanced}
|
||||
onChange={linkState(this, 'showAdvanced')}
|
||||
/>
|
||||
Advanced settings
|
||||
Show advanced settings
|
||||
</label>
|
||||
<Expander>
|
||||
{showAdvanced && (
|
||||
@@ -279,8 +278,7 @@ export class Options extends Component<Props, State> {
|
||||
<option value={Csp.kYIQ}>YIQ</option>
|
||||
</Select>
|
||||
</label>
|
||||
<label class={style.optionToggle}>
|
||||
Random matrix
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
checked={useRandomMatrix}
|
||||
onChange={this._inputChange(
|
||||
@@ -288,6 +286,7 @@ export class Options extends Component<Props, State> {
|
||||
'boolean',
|
||||
)}
|
||||
/>
|
||||
Random matrix
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,25 +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.
|
||||
*/
|
||||
export interface Options {
|
||||
left: number;
|
||||
right: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
}
|
||||
|
||||
export const defaultOptions: Options = {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
};
|
||||
@@ -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,63 +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 { Options } from '../shared/meta';
|
||||
|
||||
export default async function crop(
|
||||
data: ImageData,
|
||||
opts: Options,
|
||||
): Promise<ImageData> {
|
||||
const { left, top, right, bottom } = opts;
|
||||
const source = data.data;
|
||||
const { width, height } = data;
|
||||
const cols = width * 4;
|
||||
|
||||
const newWidth = width - left - right;
|
||||
const newHeight = height - top - bottom;
|
||||
const len = newWidth * newHeight * 4;
|
||||
const pixels = new Uint8ClampedArray(len);
|
||||
|
||||
for (let y = 0; y < newHeight; y++) {
|
||||
for (let x = 0; x < newWidth; x++) {
|
||||
let i = y * cols + x * 4;
|
||||
let j = (top + y) * cols + (left + x) * 4;
|
||||
pixels[i] = source[j];
|
||||
pixels[i + 1] = source[j + 1];
|
||||
pixels[i + 2] = source[j + 2];
|
||||
pixels[i + 3] = source[j + 3];
|
||||
}
|
||||
}
|
||||
|
||||
// let sourceX = left;
|
||||
// let sourceY = top;
|
||||
// let x = 0;
|
||||
// let y = 0;
|
||||
// let i = 0;
|
||||
// while (i < len) {
|
||||
// let from = sourceY * cols + sourceX * 4;
|
||||
|
||||
// pixels[i++] = source[from++];
|
||||
// pixels[i++] = source[from++];
|
||||
// pixels[i++] = source[from++];
|
||||
// pixels[i++] = source[from];
|
||||
|
||||
// if (++x === newWidth) {
|
||||
// x = 0;
|
||||
// y++;
|
||||
|
||||
// sourceX = left;
|
||||
// sourceY++;
|
||||
// }
|
||||
// }
|
||||
|
||||
return new ImageData(pixels, newWidth, newHeight);
|
||||
}
|
||||
@@ -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,21 +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.
|
||||
*/
|
||||
export interface Options {
|
||||
horizontal: boolean;
|
||||
vertical: boolean;
|
||||
}
|
||||
|
||||
export const defaultOptions: Options = {
|
||||
horizontal: false,
|
||||
vertical: false,
|
||||
};
|
||||
@@ -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,68 +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 { Options } from '../shared/meta';
|
||||
|
||||
export default async function flip(
|
||||
data: ImageData,
|
||||
opts: Options,
|
||||
): Promise<ImageData> {
|
||||
const { vertical, horizontal } = opts;
|
||||
const source = data.data;
|
||||
const len = source.length;
|
||||
const pixels = new Uint8ClampedArray(len);
|
||||
const { width, height } = data;
|
||||
|
||||
let i = 0;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
const cols = width * 4;
|
||||
while (i < len) {
|
||||
let from = vertical ? (height - y) * cols + x * 4 : i;
|
||||
if (horizontal) from = from - x * 4 + cols - x * 4; // todo: reduce
|
||||
|
||||
pixels[i++] = source[from++];
|
||||
pixels[i++] = source[from++];
|
||||
pixels[i++] = source[from++];
|
||||
pixels[i++] = source[from];
|
||||
|
||||
if (++x === width) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function swap(a: number, b: number) {
|
||||
let tmp = pixels[a];
|
||||
pixels[a] = pixels[b];
|
||||
pixels[b] = tmp;
|
||||
}
|
||||
function swapRgba(a: number, b: number) {
|
||||
swap(a, b);
|
||||
swap(a+1, b+1);
|
||||
swap(a+2, b+2);
|
||||
swap(a+3, b+3);
|
||||
}
|
||||
const COLS = data.width * 4;
|
||||
// for (let y = 0, y2 = (data.height - 1); y < y2; y+=4, y2-=4) {
|
||||
for (let y = 0; y < data.height; y++) {
|
||||
for (let x = 0, x2 = COLS - 4; x < x2; x+=4, x2-=4) {
|
||||
const offsetX = y * COLS;
|
||||
const offsetY = (opts.vertical ? (data.height - y) : y) * COLS;
|
||||
const flippedX = opts.horizontal ? x2 : x;
|
||||
swapRgba(offsetX + x, offsetY + x2);
|
||||
}
|
||||
}
|
||||
*/
|
||||
return new ImageData(pixels, data.width, data.height);
|
||||
}
|
||||
@@ -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" />
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
inputFieldChecked,
|
||||
} from 'client/lazy-app/util';
|
||||
import * as style from 'client/lazy-app/Compress/Options/style.css';
|
||||
import { linkRef } from 'shared/prerendered-app/util';
|
||||
import { linkRef } from 'shared/initial-app/util';
|
||||
import Select from 'client/lazy-app/Compress/Options/Select';
|
||||
import Expander from 'client/lazy-app/Compress/Options/Expander';
|
||||
import Checkbox from 'client/lazy-app/Compress/Options/Checkbox';
|
||||
@@ -285,33 +285,33 @@ export class Options extends Component<Props, State> {
|
||||
</label>
|
||||
<Expander>
|
||||
{isWorkerOptions(options) ? (
|
||||
<label class={style.optionToggle}>
|
||||
Premultiply alpha channel
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="premultiply"
|
||||
checked={options.premultiply}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Premultiply alpha channel
|
||||
</label>
|
||||
) : null}
|
||||
{isWorkerOptions(options) ? (
|
||||
<label class={style.optionToggle}>
|
||||
Linear RGB
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="linearRGB"
|
||||
checked={options.linearRGB}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Linear RGB
|
||||
</label>
|
||||
) : null}
|
||||
</Expander>
|
||||
<label class={style.optionToggle}>
|
||||
Maintain aspect ratio
|
||||
<label class={style.optionInputFirst}>
|
||||
<Checkbox
|
||||
name="maintainAspect"
|
||||
checked={maintainAspect}
|
||||
onChange={linkState(this, 'maintainAspect')}
|
||||
/>
|
||||
Maintain aspect ratio
|
||||
</label>
|
||||
<Expander>
|
||||
{maintainAspect ? null : (
|
||||
|
||||
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 2.8 MiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 2.7 MiB |
BIN
src/shared/initial-app/Intro/imgs/demos/icon-demo-artwork.jpg
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
BIN
src/shared/initial-app/Intro/imgs/demos/icon-demo-logo.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
255
src/shared/initial-app/Intro/index.tsx
Normal file
@@ -0,0 +1,255 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { linkRef } from 'shared/initial-app/util';
|
||||
import '../custom-els/loading-spinner';
|
||||
|
||||
import logo from 'url:./imgs/logo.svg';
|
||||
import largePhoto from 'url:./imgs/demos/demo-large-photo.jpg';
|
||||
import artwork from 'url:./imgs/demos/demo-artwork.jpg';
|
||||
import deviceScreen from 'url:./imgs/demos/demo-device-screen.png';
|
||||
import largePhotoIcon from 'url:./imgs/demos/icon-demo-large-photo.jpg';
|
||||
import artworkIcon from 'url:./imgs/demos/icon-demo-artwork.jpg';
|
||||
import deviceScreenIcon from 'url:./imgs/demos/icon-demo-device-screen.jpg';
|
||||
import logoIcon from 'url:./imgs/demos/icon-demo-logo.png';
|
||||
import * as style from './style.css';
|
||||
import type SnackBarElement from 'shared/initial-app/custom-els/snack-bar';
|
||||
import 'shared/initial-app/custom-els/snack-bar';
|
||||
|
||||
const demos = [
|
||||
{
|
||||
description: 'Large photo (2.8mb)',
|
||||
filename: 'photo.jpg',
|
||||
url: largePhoto,
|
||||
iconUrl: largePhotoIcon,
|
||||
},
|
||||
{
|
||||
description: 'Artwork (2.9mb)',
|
||||
filename: 'art.jpg',
|
||||
url: artwork,
|
||||
iconUrl: artworkIcon,
|
||||
},
|
||||
{
|
||||
description: 'Device screen (1.6mb)',
|
||||
filename: 'pixel3.png',
|
||||
url: deviceScreen,
|
||||
iconUrl: deviceScreenIcon,
|
||||
},
|
||||
{
|
||||
description: 'SVG icon (13k)',
|
||||
filename: 'squoosh.svg',
|
||||
url: logo,
|
||||
iconUrl: logoIcon,
|
||||
},
|
||||
];
|
||||
|
||||
const installButtonSource = 'introInstallButton-Purple';
|
||||
|
||||
interface Props {
|
||||
onFile?: (file: File) => 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();
|
||||
|
||||
if (__PRERENDER__) return;
|
||||
// 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);
|
||||
}
|
||||
|
||||
private resetFileInput = () => {
|
||||
this.fileInput!.value = '';
|
||||
};
|
||||
|
||||
private onFileChange = (event: Event): void => {
|
||||
const fileInput = event.target as HTMLInputElement;
|
||||
const file = fileInput.files && fileInput.files[0];
|
||||
if (!file) return;
|
||||
this.resetFileInput();
|
||||
this.props.onFile!(file);
|
||||
};
|
||||
|
||||
private onButtonClick = () => {
|
||||
this.fileInput!.click();
|
||||
};
|
||||
|
||||
private onDemoClick = async (index: number, event: Event) => {
|
||||
try {
|
||||
this.setState({ fetchingDemoIndex: index });
|
||||
const demo = demos[index];
|
||||
const blob = await fetch(demo.url).then((r) => r.blob());
|
||||
|
||||
// Firefox doesn't like content types like 'image/png; charset=UTF-8', which Webpack's dev
|
||||
// server returns. https://bugzilla.mozilla.org/show_bug.cgi?id=1497925.
|
||||
const type = /[^;]*/.exec(blob.type)![0];
|
||||
const file = new File([blob], demo.filename, { type });
|
||||
this.props.onFile!(file);
|
||||
} catch (err) {
|
||||
this.setState({ fetchingDemoIndex: undefined });
|
||||
this.props.showSnack!("Couldn't fetch demo image");
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
private onInstallClick = async (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;
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
<div class={style.logoSizer}>
|
||||
<div class={style.logoContainer}>
|
||||
<img
|
||||
src={logo}
|
||||
class={style.logo}
|
||||
alt="Squoosh"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class={style.openImageGuide}>
|
||||
Drag & drop or{' '}
|
||||
<button class={style.selectButton} onClick={this.onButtonClick}>
|
||||
select an image
|
||||
</button>
|
||||
<input
|
||||
class={style.hide}
|
||||
ref={linkRef(this, 'fileInput')}
|
||||
type="file"
|
||||
onChange={this.onFileChange}
|
||||
/>
|
||||
</p>
|
||||
<p>Or try one of these:</p>
|
||||
<ul class={style.demos}>
|
||||
{demos.map((demo, i) => (
|
||||
<li key={demo.url} class={style.demoItem}>
|
||||
<button
|
||||
class={style.demoButton}
|
||||
onClick={this.onDemoClick.bind(this, i)}
|
||||
>
|
||||
<div class={style.demo}>
|
||||
<div class={style.demoImgContainer}>
|
||||
<div class={style.demoImgAspect}>
|
||||
<img
|
||||
class={style.demoIcon}
|
||||
src={demo.iconUrl}
|
||||
alt=""
|
||||
decoding="async"
|
||||
/>
|
||||
{fetchingDemoIndex === i && (
|
||||
<div class={style.demoLoading}>
|
||||
<loading-spinner class={style.demoLoadingSpinner} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div class={style.demoDescription}>{demo.description}</div>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</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/dev/README.md#privacy">
|
||||
Privacy
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -29,12 +29,3 @@ interface BeforeInstallPromptEvent extends Event {
|
||||
interface WindowEventMap {
|
||||
beforeinstallprompt: BeforeInstallPromptEvent;
|
||||
}
|
||||
|
||||
interface ClipboardItem {
|
||||
types: string[];
|
||||
getType(type: string): Promise<Blob>;
|
||||
}
|
||||
|
||||
interface Clipboard {
|
||||
read(): Promise<ClipboardItem[]>;
|
||||
}
|
||||
228
src/shared/initial-app/Intro/style.css
Normal file
@@ -0,0 +1,228 @@
|
||||
@font-face {
|
||||
font-family: 'intro-text';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: block;
|
||||
/* This only contains the chars for "Drag & drop or" */
|
||||
src: url('data:font/woff2;base64,d09GMgABAAAAAAXcAA4AAAAACowAAAWJAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbg2gcMAZgAGwRCAqIQIcnCxYAATYCJAMoBCAFgwAHIBvqCFEU84FMI2Xh/P3g+Tfn532yQ/IgYz4BrJyhtkkZwFBYAZ49sI63e5v/NnqzIfbADyE0qxOqK8ESLoNNdULHihxbW0W86/qHEk4wT/eHShPRZJYYqUGkQdLSWCeSemZBzwpKyX/LRoAhMEQhqCFBw5RHNCc4hbVn35FsxtTXVHYyo7miu5VN2AW1fwzVauRgXnIGo2IWsYdViUoLu6mms5VFAn+SeQ4eBazfj7QodrMt4oyQHaGADEPRpTbDqJaoTENNK6DpOralUszf6gI/QsAhWZSMKVOirikSJxZRLBVD0S4mB0kTBRwopjZ/mt/2/25+bcSipgiHRmwiFI1g+XhwlshyEAsbJzGiGH+U5whHNgiXooplafI1rMFbmIqjGAPhmcSkVFxeu9hw87aXsGyL+dPE05qUpK2WyaVQcZVW+aDmw3aalLJKNmQORcpZYtBIuTrncN4xXoVZY617TBSsx2T1DHgGU6u4etE04wha1GEwjVkEaDttOrl1FCOwUMxgHnuooJo62ukcWEuc1/aT+dZ8b142t5tbzc3mGnP1EJqVTEGMYTjG14YxtGEEG+0E2axhe6Oa1E8UrDHDFjhTRywYNWrU9JHTlw7RmaslkrrGcTJ+znW4EzzP0zovE4Z5d0hqVhBobftBIKkwL09SOv3hhCuv1Dp9taTeCJ2Mj3KDT8iDng5DkWzPw/UdP8idNDkMnUyOwEauwnYLQeLC7GskNe72QKe97AmuA42E5FjfyYTM+HTdQ+Xqb+q4JvptyKZN1w47qMMwL58fyKZM1U6NXgWlOFdxx7DpXHDTz4UB89WMK3HH3uY7mavFopGF+u36lGlqZsL4ugmbqvZxveycMO+a4uyN3o7GT2qdHpfr6W++kNTn1crdx7Z+FW7PfffTmfnXV/2ivsh5UX93zdlzct6QlSuHSumG3oGNNT9/m9yXnDcnKfsmDx8xUaoKi+uvGs99H2ieUJUg8bTnVwQcDd/SPKwYWDUv+QkpT6MulMrcPTXNWYnIowxvoiwnX+opTMkvzOMGgpNpqnK32CNVwCnassw0BwQwTa0rLS3m1DfIoxx5PIE8SvEmSk3pHSWZiRVKjOOQSylJSHGXkhT/u/tg/Vm9UZQcS59TGb1qjcuuT0925iaaU1vaWpZJM4ukqWWlrdWSIcVNlOImvnrzLn53UpnSLzbGT5lUlpTiKiPJFEmyqywFLtOhcaYJkWkaGe/oGBlnmiIiIiKYpqHxLmdaWg5JpxxHSXpajsuVlkPSvb1JelqOC0pubbAn2A2UsDdYmTmjvbVlgTRhVBSSxpbF1nZD+jvkUR4rcJeSFBp2d19SUsVW5DjkUkoSoITHJ7iJEpZnZaL4OiF7g92DN1mz8b1RiM9RDk9ps9pcanamlnj2ftqbJpHJ0wpkRn2+RJ6qsGflpYrPnxG6A4r9zqGY3qCcqDuhsWGQhoXpQ0663cWFM4qNR0Jxj1R0UBT36pahMneH4NYV27jElOeyAAAACACAABy4uvGyOsj21Y9h3gIA3PuxYAYAuC/7vftf7L+PXunMAQDwBQIAAAIA5vR/HwCvOQ//TzLL7cPIHUC0zMI5v7+tHiVfzWOeSrJKZbFabWGNSnJE+jmsnmTjTZm6kBi9r0aLgm8qNk6t67ATuPlEitG+g+E7in1GMYxCxmIF9YzNJK7lRoSPc6PCD+8fxhp+YjdttDNAJw3UUU83M1jFClaylkpkU08NVZqkh0oaaBLPnaCTNhqpoaok2UkPZqy/JyfpKnVLkhrq6KGZCjpZxTJWqN9uJofD5HGMzSXHLaVbOmuTSnOp6cTQgJlaB6oF7RIITul8N+1sYjnL6aJqqoZ2inaxDIY2s2zwlXUs5zj7OPJmAPao+ZhVHy0A')
|
||||
format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'intro-text';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: block;
|
||||
/* Only contains the chars for "select an image" */
|
||||
src: url('data:font/woff2;base64,d09GMgABAAAAAAXMAA4AAAAACwQAAAV5AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbg1IcMAZgAGwRCAqJUId/CxoAATYCJAMwBCAFgnAHIBskCcjEh6dNff8Ou/9Tj9VZGnUhJeqFzWGiVVOkxkTthr9f6kWkRdsBbkIj3YuLaloFZWr7aBg22z7IOoWqBWCW5cZU3GBrh0n+dAcBYAUlzYHAzWTYchqKXEyAT0zOLIS1qqm+B8q2ur4OhEMy0PHHUH8KklaSr8T0mp/EU7kRvXlI1E09HXA1qLN8Djxa0AsSDOg3cJARb9mtQJoSK3gvEn372/gcAigg/gOnbsT/MYv491GTReW4rJC5LA+h5FFclF6QQgoZ5Kx7GbsuGeytUgFClkOomY2Gdake3m9HegkHieAx/a0hBTALsy4jvpxBcnFXUnjC+2ZS5zHnDeEaJVwi+ZWqzOm4Uvgy4k6kGv4kFDVkfjk1gkVRRkk2zlo42PBbRJmG30cClJQjak7BnfQqHza4ITKftQZ/ZMUaEiyy1+mYCh4clKhDA5rQglZ0oG9jw+qiNvT+SfxIXCeuFdeIq8VV4gpxyRaGOl0JiChiCocfc7e93DwZIPvWgPiZJLcJugxyjW7UQyl4TJk6dWqYU7Cn0WQiWnNJCdGeprqjW63fpVS3mKy4YGZ6I3ya4nbIVgM1mwkpNEBzixlNxfPmH7owvdE4973OM9quvk11dwvnzDUy/Zn5S5Ywpn/PeqXBQI2m4lna05CRtsI6+GIENjS9K4jWRHUGYA2ozdZm2Smmf0DI3aqpeNbsJfxe7YdFmcZAn5gXLCFa2/Umqiu017APFhMZ0rfQp4sJX0ZrJ+n9UtAljr5VYWb6oj1MrpvX3qe6u8WRJg0bj7aPkDOa7m+E0Oa9Y8eY/gbRbr+efH7hcO49bMd28fbDVHcUmm3XkozQGKjeBHSJ4TQnI879LIFmF2v/BJuEQlffJPfE9oKayS/PsPE44fvM4MsBESxbuEEV39d5pw6oW4vD6S1WQC3UpSbHNbK0Jikl0bphSs+0CGW8Ew4Kzw7zarmcVz873JHTFhKYay18R8vY0ozPiHPAGyROAqlW5fLj5+HbWBn9TpgekKsOy8N+4dlFfL9i/Nk3+gY1bwzZUAvLVNiFpvqHRenetSoVrgn2obGtPsltEVxEeHJAQFhyBIcHT9rDvTJm06e0TLgase2gd2RffGJNg0o1zrdRyi9s1bYE5bi85cK+o/nUwvBR5+jweEBaSMoCub29fEFISmib9Dn5yl5kVFpoGrPQSmZhafQ7WimttNCH7Cktohb6kFpoEfIsdDF7SgvZTbaY3mKFyLXQh+wpzWE3mUNQQkWnR+lgiW9Afkunej49Nz3sYlI8XFTRdkNhUR5d6h4oOpJc8OjcItMXVqoHW2fSW6ycWuiM8NDYoICouLAZ9BYrwwmhycvKlt4Q8hUlCV5nZ7vOm2ut2rizcFpuWpSrT3K1Z3xinbuHnXBTyGAljV/XzHZaNGu6y7vLDziMpIyUdOBBRXXlxznUQiOoheZsZk9njG8er1mNmz2eOCQ3x9BbLP+Zxt+VrbEz9aWxmimRvyl4/sumyoM/nw+LNzV4/uP0/9T/P5f08cvhl38USAS/xTYs2fL/VNFF0vd+SVsRB/khPwW4SCi5SHhx8fDgVPAiAqIJRQL/EuK5bsRzNnAiezD1i7u2VyHHAKRU+E2YUaA5DUsE2ZfbApAmsJcxjBwmQ2Xk4Y2BqZJ+oxSzsNEogz1O3/xkBMKEBHSiC8PoQStaoEIflPCHL/wQBCUKoUITlMhHP+olt5ojykUPOrEQTWgY+f449KMPKnSiB72jGrLQhEZO24925LtbkG5DndTkD2/4um+NQBEyUIJsRBxX24tcDGmbWtGJjq05jhzTaMgCcR+6EA4f+KAXDay5u6RsL7xJsm3w3mzvFvggB8nI/AYBdVnxNvx/2wA=')
|
||||
format('woff2');
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.intro {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr min-content;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow: auto;
|
||||
padding: 20px 0 0;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
overscroll-behavior: contain;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
position: relative;
|
||||
padding-top: 100%;
|
||||
}
|
||||
|
||||
.logo-sizer {
|
||||
width: 90%;
|
||||
max-width: 52vh;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
composes: abs-fill from '../util.css';
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.open-image-guide {
|
||||
font: 300 11vw intro-text, sans-serif;
|
||||
margin-bottom: 0;
|
||||
|
||||
@media (min-width: 460px) {
|
||||
font-size: 50.6px;
|
||||
padding: 0 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-button {
|
||||
composes: unbutton from '../util.css';
|
||||
font-weight: 500;
|
||||
color: #5d509e;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.demos {
|
||||
display: block;
|
||||
padding: 0;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
margin: 0 auto;
|
||||
|
||||
@media (min-width: 400px) {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: 580px) {
|
||||
border-top: none;
|
||||
width: 523px;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
width: 773px;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-item {
|
||||
background: #fff;
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
|
||||
@media (min-width: 580px) {
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 4px;
|
||||
margin: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-button {
|
||||
composes: unbutton from '../util.css';
|
||||
flex: 1;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.demo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 7px;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.demo-img-container {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
width: 47px;
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.demo-img-aspect {
|
||||
position: relative;
|
||||
padding-top: 100%;
|
||||
}
|
||||
|
||||
.demo-icon {
|
||||
composes: abs-fill from '../util.css';
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.demo-description {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.demo-loading {
|
||||
composes: abs-fill from '../util.css';
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: fade-in 300ms ease-in-out;
|
||||
}
|
||||
|
||||
.demo-loading-spinner {
|
||||
--color: #fff;
|
||||
}
|
||||
|
||||
.install-button {
|
||||
composes: unbutton from '../util.css';
|
||||
|
||||
&: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 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.related-links {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
font-size: 1.3rem;
|
||||
|
||||
& li {
|
||||
display: block;
|
||||
border-left: 1px solid #000;
|
||||
padding: 0 0.6em;
|
||||
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
& a:link {
|
||||
color: #5d509e;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as styles from './styles.css';
|
||||
import 'add-css:./styles.css';
|
||||
|
||||
// So it doesn't cause an error when running in node
|
||||
const HTMLEl = ((__PRERENDER__
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as style from './styles.css';
|
||||
import 'add-css:./styles.css';
|
||||
|
||||
// So it doesn't cause an error when running in node
|
||||
const HTMLEl = ((__PRERENDER__
|
||||
22
src/shared/initial-app/util.css
Normal file
@@ -0,0 +1,22 @@
|
||||
.abs-fill {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.unbutton {
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
font: inherit;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
21
src/shared/missing-types.d.ts
vendored
@@ -13,24 +13,3 @@
|
||||
/// <reference path="../../missing-types.d.ts" />
|
||||
|
||||
declare const __PRERENDER__: boolean;
|
||||
|
||||
type ResizeObserverCallback = (
|
||||
entries: ResizeObserverEntry[],
|
||||
observer: ResizeObserver,
|
||||
) => void;
|
||||
|
||||
interface ResizeObserverEntry {
|
||||
readonly target: Element;
|
||||
readonly contentRect: DOMRectReadOnly;
|
||||
}
|
||||
|
||||
interface ResizeObserver {
|
||||
observe(target: Element): void;
|
||||
unobserve(target: Element): void;
|
||||
disconnect(): void;
|
||||
}
|
||||
|
||||
declare var ResizeObserver: {
|
||||
prototype: ResizeObserver;
|
||||
new (callback: ResizeObserverCallback): ResizeObserver;
|
||||
};
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
import * as style from '../style.css';
|
||||
import { startBlobs } from './meta';
|
||||
|
||||
/**
|
||||
* Control point x,y - point x,y - control point x,y
|
||||
*/
|
||||
export type BlobPoint = [number, number, number, number, number, number];
|
||||
|
||||
const maxPointDistance = 0.25;
|
||||
|
||||
function randomisePoint(point: BlobPoint): BlobPoint {
|
||||
const distance = Math.random() * maxPointDistance;
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const xShift = Math.sin(angle) * distance;
|
||||
const yShift = Math.cos(angle) * distance;
|
||||
return [
|
||||
point[0] + xShift,
|
||||
point[1] + yShift,
|
||||
point[2] + xShift,
|
||||
point[3] + yShift,
|
||||
point[4] + xShift,
|
||||
point[5] + yShift,
|
||||
];
|
||||
}
|
||||
|
||||
function easeInOutQuad(x: number): number {
|
||||
return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
|
||||
}
|
||||
|
||||
function easeInExpo(x: number): number {
|
||||
return x === 0 ? 0 : Math.pow(2, 10 * x - 10);
|
||||
}
|
||||
|
||||
const rand = (min: number, max: number) => Math.random() * (max - min) + min;
|
||||
|
||||
interface CircleBlobPointState {
|
||||
basePoint: BlobPoint;
|
||||
pos: number;
|
||||
duration: number;
|
||||
startPoint: BlobPoint;
|
||||
endPoint: BlobPoint;
|
||||
}
|
||||
|
||||
/** Bezier points for a seven point circle, to 3 decimal places */
|
||||
const sevenPointCircle: BlobPoint[] = [
|
||||
[-0.304, -1, 0, -1, 0.304, -1],
|
||||
[0.592, -0.861, 0.782, -0.623, 0.972, -0.386],
|
||||
[1.043, -0.074, 0.975, 0.223, 0.907, 0.519],
|
||||
[0.708, 0.769, 0.434, 0.901, 0.16, 1.033],
|
||||
[-0.16, 1.033, -0.434, 0.901, -0.708, 0.769],
|
||||
[-0.907, 0.519, -0.975, 0.223, -1.043, -0.074],
|
||||
[-0.972, -0.386, -0.782, -0.623, -0.592, -0.861],
|
||||
];
|
||||
|
||||
/*
|
||||
// Should it be needed, here's how the above was created:
|
||||
function createBezierCirclePoints(points: number): BlobPoint[] {
|
||||
const anglePerPoint = 360 / points;
|
||||
const matrix = new DOMMatrix();
|
||||
const point = new DOMPoint();
|
||||
const controlDistance = (4 / 3) * Math.tan(Math.PI / (2 * points));
|
||||
return Array.from({ length: points }, (_, i) => {
|
||||
point.x = -controlDistance;
|
||||
point.y = -1;
|
||||
const cp1 = point.matrixTransform(matrix);
|
||||
point.x = 0;
|
||||
point.y = -1;
|
||||
const p = point.matrixTransform(matrix);
|
||||
point.x = controlDistance;
|
||||
point.y = -1;
|
||||
const cp2 = point.matrixTransform(matrix);
|
||||
const basePoint: BlobPoint = [cp1.x, cp1.y, p.x, p.y, cp2.x, cp2.y];
|
||||
matrix.rotateSelf(0, 0, anglePerPoint);
|
||||
return basePoint;
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
interface CircleBlobOptions {
|
||||
minDuration?: number;
|
||||
maxDuration?: number;
|
||||
startPoints?: BlobPoint[];
|
||||
}
|
||||
|
||||
class CircleBlob {
|
||||
private animStates: CircleBlobPointState[];
|
||||
private minDuration: number;
|
||||
private maxDuration: number;
|
||||
private points: BlobPoint[];
|
||||
|
||||
constructor(
|
||||
basePoints: BlobPoint[],
|
||||
{
|
||||
startPoints = basePoints.map((point) => randomisePoint(point)),
|
||||
minDuration = 4000,
|
||||
maxDuration = 11000,
|
||||
}: CircleBlobOptions = {},
|
||||
) {
|
||||
this.points = startPoints;
|
||||
this.minDuration = minDuration;
|
||||
this.maxDuration = maxDuration;
|
||||
this.animStates = basePoints.map((basePoint, i) => ({
|
||||
basePoint,
|
||||
pos: 0,
|
||||
duration: rand(minDuration, maxDuration),
|
||||
startPoint: startPoints[i],
|
||||
endPoint: randomisePoint(basePoint),
|
||||
}));
|
||||
}
|
||||
|
||||
advance(timeDelta: number): void {
|
||||
this.points = this.animStates.map((animState) => {
|
||||
animState.pos += timeDelta / animState.duration;
|
||||
if (animState.pos >= 1) {
|
||||
animState.startPoint = animState.endPoint;
|
||||
animState.pos = 0;
|
||||
animState.duration = rand(this.minDuration, this.maxDuration);
|
||||
animState.endPoint = randomisePoint(animState.basePoint);
|
||||
}
|
||||
const eased = easeInOutQuad(animState.pos);
|
||||
|
||||
const point = animState.startPoint.map((startPoint, i) => {
|
||||
const endPoint = animState.endPoint[i];
|
||||
return (endPoint - startPoint) * eased + startPoint;
|
||||
}) as BlobPoint;
|
||||
|
||||
return point;
|
||||
});
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D) {
|
||||
const points = this.points;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[0][2], points[0][3]);
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
const nextI = i === points.length - 1 ? 0 : i + 1;
|
||||
ctx.bezierCurveTo(
|
||||
points[i][4],
|
||||
points[i][5],
|
||||
points[nextI][0],
|
||||
points[nextI][1],
|
||||
points[nextI][2],
|
||||
points[nextI][3],
|
||||
);
|
||||
}
|
||||
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
const centralBlobsRotationTime = 120000;
|
||||
|
||||
class CentralBlobs {
|
||||
private rotatePos: number = 0;
|
||||
private blobs = Array.from(
|
||||
{ length: 4 },
|
||||
(_, i) => new CircleBlob(sevenPointCircle, { startPoints: startBlobs[i] }),
|
||||
);
|
||||
|
||||
advance(timeDelta: number) {
|
||||
this.rotatePos =
|
||||
(this.rotatePos + timeDelta / centralBlobsRotationTime) % 1;
|
||||
for (const blob of this.blobs) blob.advance(timeDelta);
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D, x: number, y: number, radius: number) {
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.scale(radius, radius);
|
||||
ctx.rotate(Math.PI * 2 * this.rotatePos);
|
||||
for (const blob of this.blobs) blob.draw(ctx);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
const bgBlobsMinRadius = 7;
|
||||
const bgBlobsMaxRadius = 60;
|
||||
const bgBlobsMinAlpha = 0.2;
|
||||
const bgBlobsMaxAlpha = 0.8;
|
||||
const bgBlobsPerPx = 0.000025;
|
||||
const bgBlobsMinSpinTime = 20000;
|
||||
const bgBlobsMaxSpinTime = 60000;
|
||||
const bgBlobsMinVelocity = 0.0015;
|
||||
const bgBlobsMaxVelocity = 0.007;
|
||||
const gravityVelocityMultiplier = 15;
|
||||
const gravityStartDistance = 300;
|
||||
|
||||
interface BackgroundBlob {
|
||||
blob: CircleBlob;
|
||||
velocity: number;
|
||||
spinTime: number;
|
||||
alpha: number;
|
||||
alphaMultiplier: number;
|
||||
rotatePos: number;
|
||||
radius: number;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
const bgBlobsAlphaTime = 2000;
|
||||
|
||||
class BackgroundBlobs {
|
||||
private bgBlobs: BackgroundBlob[] = [];
|
||||
private overallAlphaPos = 0;
|
||||
|
||||
constructor(bounds: DOMRect) {
|
||||
const blobs = Math.round(bounds.width * bounds.height * bgBlobsPerPx);
|
||||
this.bgBlobs = Array.from({ length: blobs }, () => {
|
||||
const radiusPos = easeInExpo(Math.random());
|
||||
|
||||
return {
|
||||
blob: new CircleBlob(sevenPointCircle, {
|
||||
minDuration: 2000,
|
||||
maxDuration: 5000,
|
||||
}),
|
||||
// Velocity is based on the size
|
||||
velocity:
|
||||
(1 - radiusPos) * (bgBlobsMaxVelocity - bgBlobsMinVelocity) +
|
||||
bgBlobsMinVelocity,
|
||||
alpha:
|
||||
Math.random() ** 3 * (bgBlobsMaxAlpha - bgBlobsMinAlpha) +
|
||||
bgBlobsMinAlpha,
|
||||
alphaMultiplier: 1,
|
||||
spinTime: rand(bgBlobsMinSpinTime, bgBlobsMaxSpinTime),
|
||||
rotatePos: 0,
|
||||
radius:
|
||||
radiusPos * (bgBlobsMaxRadius - bgBlobsMinRadius) + bgBlobsMinRadius,
|
||||
x: Math.random() * bounds.width,
|
||||
y: Math.random() * bounds.height,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
advance(
|
||||
timeDelta: number,
|
||||
bounds: DOMRect,
|
||||
targetX: number,
|
||||
targetY: number,
|
||||
targetRadius: number,
|
||||
) {
|
||||
if (this.overallAlphaPos !== 1) {
|
||||
this.overallAlphaPos = Math.min(
|
||||
1,
|
||||
this.overallAlphaPos + timeDelta / bgBlobsAlphaTime,
|
||||
);
|
||||
}
|
||||
for (const bgBlob of this.bgBlobs) {
|
||||
bgBlob.blob.advance(timeDelta);
|
||||
let dist = Math.hypot(bgBlob.x - targetX, bgBlob.y - targetY);
|
||||
bgBlob.rotatePos = (bgBlob.rotatePos + timeDelta / bgBlob.spinTime) % 1;
|
||||
|
||||
if (dist < 10) {
|
||||
// Move the circle out to a random edge
|
||||
switch (Math.floor(Math.random() * 4)) {
|
||||
case 0: // top
|
||||
bgBlob.x = Math.random() * bounds.width;
|
||||
bgBlob.y = -(bgBlob.radius * (1 + maxPointDistance));
|
||||
break;
|
||||
case 1: // left
|
||||
bgBlob.x = -(bgBlob.radius * (1 + maxPointDistance));
|
||||
bgBlob.y = Math.random() * bounds.height;
|
||||
break;
|
||||
case 2: // bottom
|
||||
bgBlob.x = Math.random() * bounds.width;
|
||||
bgBlob.y = bounds.height + bgBlob.radius * (1 + maxPointDistance);
|
||||
break;
|
||||
case 3: // right
|
||||
bgBlob.x = bounds.width + bgBlob.radius * (1 + maxPointDistance);
|
||||
bgBlob.y = Math.random() * bounds.height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dist = Math.hypot(bgBlob.x - targetX, bgBlob.y - targetY);
|
||||
const velocity =
|
||||
dist > gravityStartDistance
|
||||
? bgBlob.velocity
|
||||
: ((1 - dist / gravityStartDistance) *
|
||||
(gravityVelocityMultiplier - 1) +
|
||||
1) *
|
||||
bgBlob.velocity;
|
||||
const shiftDist = velocity * timeDelta;
|
||||
const direction = Math.atan2(targetX - bgBlob.x, targetY - bgBlob.y);
|
||||
const xShift = Math.sin(direction) * shiftDist;
|
||||
const yShift = Math.cos(direction) * shiftDist;
|
||||
bgBlob.x += xShift;
|
||||
bgBlob.y += yShift;
|
||||
bgBlob.alphaMultiplier = Math.min(dist / targetRadius, 1);
|
||||
}
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D) {
|
||||
const overallAlpha = easeInOutQuad(this.overallAlphaPos);
|
||||
|
||||
for (const bgBlob of this.bgBlobs) {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = bgBlob.alpha * bgBlob.alphaMultiplier * overallAlpha;
|
||||
ctx.translate(bgBlob.x, bgBlob.y);
|
||||
ctx.scale(bgBlob.radius, bgBlob.radius);
|
||||
ctx.rotate(Math.PI * 2 * bgBlob.rotatePos);
|
||||
bgBlob.blob.draw(ctx);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deltaMultiplierStep = 0.01;
|
||||
|
||||
export function startBlobAnim(canvas: HTMLCanvasElement) {
|
||||
let lastTime: number;
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
const centralBlobs = new CentralBlobs();
|
||||
let backgroundBlobs: BackgroundBlobs;
|
||||
const loadImgEl = document.querySelector('.' + style.loadImg)!;
|
||||
let hasFocus = document.hasFocus();
|
||||
let deltaMultiplier = hasFocus ? 1 : 0;
|
||||
let animating = true;
|
||||
|
||||
const visibilityListener = () => {
|
||||
// 'Pause time' while page is hidden
|
||||
if (document.visibilityState === 'visible') lastTime = performance.now();
|
||||
};
|
||||
const focusListener = () => {
|
||||
hasFocus = true;
|
||||
if (!animating) startAnim();
|
||||
};
|
||||
const blurListener = () => {
|
||||
hasFocus = false;
|
||||
};
|
||||
|
||||
new ResizeObserver(() => {
|
||||
// Redraw for new canvas size
|
||||
if (!animating) drawFrame(0);
|
||||
}).observe(canvas);
|
||||
|
||||
addEventListener('focus', focusListener);
|
||||
addEventListener('blur', blurListener);
|
||||
document.addEventListener('visibilitychange', visibilityListener);
|
||||
|
||||
function destruct() {
|
||||
removeEventListener('focus', focusListener);
|
||||
removeEventListener('blur', blurListener);
|
||||
document.removeEventListener('visibilitychange', visibilityListener);
|
||||
}
|
||||
|
||||
function drawFrame(delta: number) {
|
||||
const canvasBounds = canvas.getBoundingClientRect();
|
||||
canvas.width = canvasBounds.width * devicePixelRatio;
|
||||
canvas.height = canvasBounds.height * devicePixelRatio;
|
||||
const loadImgBounds = loadImgEl.getBoundingClientRect();
|
||||
const computedStyles = getComputedStyle(canvas);
|
||||
const blobPink = computedStyles.getPropertyValue('--blob-pink');
|
||||
const loadImgCenterX =
|
||||
loadImgBounds.left - canvasBounds.left + loadImgBounds.width / 2;
|
||||
const loadImgCenterY =
|
||||
loadImgBounds.top - canvasBounds.top + loadImgBounds.height / 2;
|
||||
const loadImgRadius = loadImgBounds.height / 2 / (1 + maxPointDistance);
|
||||
|
||||
ctx.scale(devicePixelRatio, devicePixelRatio);
|
||||
|
||||
if (!backgroundBlobs) backgroundBlobs = new BackgroundBlobs(canvasBounds);
|
||||
backgroundBlobs.advance(
|
||||
delta,
|
||||
canvasBounds,
|
||||
loadImgCenterX,
|
||||
loadImgCenterY,
|
||||
loadImgRadius,
|
||||
);
|
||||
centralBlobs.advance(delta);
|
||||
|
||||
ctx.globalAlpha = Number(
|
||||
computedStyles.getPropertyValue('--center-blob-opacity'),
|
||||
);
|
||||
ctx.fillStyle = blobPink;
|
||||
|
||||
backgroundBlobs.draw(ctx);
|
||||
centralBlobs.draw(ctx, loadImgCenterX, loadImgCenterY, loadImgRadius);
|
||||
}
|
||||
|
||||
function frame(time: number) {
|
||||
// Stop the loop if the canvas is gone
|
||||
if (!canvas.isConnected) {
|
||||
destruct();
|
||||
return;
|
||||
}
|
||||
|
||||
// Be kind: If the window isn't focused, bring the animation to a stop.
|
||||
if (!hasFocus) {
|
||||
// Bring the anim to a slow stop
|
||||
deltaMultiplier = Math.max(0, deltaMultiplier - deltaMultiplierStep);
|
||||
if (deltaMultiplier === 0) {
|
||||
animating = false;
|
||||
return;
|
||||
}
|
||||
} else if (deltaMultiplier !== 1) {
|
||||
deltaMultiplier = Math.min(1, deltaMultiplier + deltaMultiplierStep);
|
||||
}
|
||||
|
||||
const delta = (time - lastTime) * deltaMultiplier;
|
||||
lastTime = time;
|
||||
|
||||
drawFrame(delta);
|
||||
|
||||
requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
function startAnim() {
|
||||
animating = true;
|
||||
requestAnimationFrame((time: number) => {
|
||||
lastTime = time;
|
||||
frame(time);
|
||||
});
|
||||
}
|
||||
|
||||
startAnim();
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import type { BlobPoint } from '.';
|
||||
|
||||
/** Start points, for the shape we use in prerender */
|
||||
export const startBlobs: BlobPoint[][] = [
|
||||
[
|
||||
[-0.232, -1.029, 0.073, -1.029, 0.377, -1.029],
|
||||
[0.565, -1.098, 0.755, -0.86, 0.945, -0.622],
|
||||
[0.917, -0.01, 0.849, 0.286, 0.782, 0.583],
|
||||
[0.85, 0.687, 0.576, 0.819, 0.302, 0.951],
|
||||
[-0.198, 1.009, -0.472, 0.877, -0.746, 0.745],
|
||||
[-0.98, 0.513, -1.048, 0.216, -1.116, -0.08],
|
||||
[-0.964, -0.395, -0.774, -0.633, -0.584, -0.871],
|
||||
],
|
||||
[
|
||||
[-0.505, -1.109, -0.201, -1.109, 0.104, -1.109],
|
||||
[0.641, -0.684, 0.831, -0.446, 1.02, -0.208],
|
||||
[1.041, 0.034, 0.973, 0.331, 0.905, 0.628],
|
||||
[0.734, 0.794, 0.46, 0.926, 0.186, 1.058],
|
||||
[-0.135, 0.809, -0.409, 0.677, -0.684, 0.545],
|
||||
[-0.935, 0.404, -1.002, 0.108, -1.07, -0.189],
|
||||
[-0.883, -0.402, -0.693, -0.64, -0.503, -0.878],
|
||||
],
|
||||
[
|
||||
[-0.376, -1.168, -0.071, -1.168, 0.233, -1.168],
|
||||
[0.732, -0.956, 0.922, -0.718, 1.112, -0.48],
|
||||
[1.173, 0.027, 1.105, 0.324, 1.038, 0.621],
|
||||
[0.707, 0.81, 0.433, 0.943, 0.159, 1.075],
|
||||
[-0.096, 1.135, -0.37, 1.003, -0.644, 0.871],
|
||||
[-0.86, 0.457, -0.927, 0.161, -0.995, -0.136],
|
||||
[-0.87, -0.516, -0.68, -0.754, -0.49, -0.992],
|
||||
],
|
||||
[
|
||||
[-0.309, -0.998, -0.004, -0.998, 0.3, -0.998],
|
||||
[0.535, -0.852, 0.725, -0.614, 0.915, -0.376],
|
||||
[1.05, -0.09, 0.982, 0.207, 0.915, 0.504],
|
||||
[0.659, 0.807, 0.385, 0.939, 0.111, 1.071],
|
||||
[-0.178, 1.048, -0.452, 0.916, -0.727, 0.784],
|
||||
[-0.942, 0.582, -1.009, 0.285, -1.077, -0.011],
|
||||
[-1.141, -0.335, -0.951, -0.573, -0.761, -0.811],
|
||||
],
|
||||
];
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29.34 28.61"><path fill="#838383" d="M14.67 0a14.67 14.67 0 00-4.64 28.59c.74.13 1-.32 1-.7l-.02-2.74c-4.08.89-4.94-1.73-4.94-1.73a3.88 3.88 0 00-1.63-2.14c-1.33-.91.1-.9.1-.9A3.08 3.08 0 016.8 21.9a3.12 3.12 0 004.27 1.22 3.12 3.12 0 01.93-1.96c-3.26-.37-6.68-1.63-6.68-7.25a5.68 5.68 0 011.5-3.94 5.27 5.27 0 01.15-3.9S8.2 5.7 11 7.58a13.9 13.9 0 017.34 0c2.8-1.9 4.03-1.5 4.03-1.5a5.27 5.27 0 01.15 3.9 5.67 5.67 0 011.5 3.93c0 5.63-3.42 6.87-6.7 7.24a3.5 3.5 0 011 2.71l-.01 4.03c0 .39.26.85 1 .7A14.67 14.67 0 0014.67 0z"/></svg>
|
||||
|
Before Width: | Height: | Size: 588 B |
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,382 +0,0 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { linkRef } from 'shared/prerendered-app/util';
|
||||
import '../../custom-els/loading-spinner';
|
||||
import logo from 'url:./imgs/logo.svg';
|
||||
import githubLogo from 'url:./imgs/github-logo.svg';
|
||||
import largePhoto from 'url:./imgs/demos/demo-large-photo.jpg';
|
||||
import artwork from 'url:./imgs/demos/demo-artwork.jpg';
|
||||
import deviceScreen from 'url:./imgs/demos/demo-device-screen.png';
|
||||
import largePhotoIcon from 'url:./imgs/demos/icon-demo-large-photo.jpg';
|
||||
import artworkIcon from 'url:./imgs/demos/icon-demo-artwork.jpg';
|
||||
import deviceScreenIcon from 'url:./imgs/demos/icon-demo-device-screen.jpg';
|
||||
import logoIcon from 'url:./imgs/demos/icon-demo-logo.png';
|
||||
import logoWithText from 'url:./imgs/logo-with-text.svg';
|
||||
import * as style from './style.css';
|
||||
import type SnackBarElement from 'shared/custom-els/snack-bar';
|
||||
import 'shared/custom-els/snack-bar';
|
||||
import { startBlobs } from './blob-anim/meta';
|
||||
|
||||
const demos = [
|
||||
{
|
||||
description: 'Large photo',
|
||||
size: '2.8mb',
|
||||
filename: 'photo.jpg',
|
||||
url: largePhoto,
|
||||
iconUrl: largePhotoIcon,
|
||||
},
|
||||
{
|
||||
description: 'Artwork',
|
||||
size: '2.9mb',
|
||||
filename: 'art.jpg',
|
||||
url: artwork,
|
||||
iconUrl: artworkIcon,
|
||||
},
|
||||
{
|
||||
description: 'Device screen',
|
||||
size: '1.6mb',
|
||||
filename: 'pixel3.png',
|
||||
url: deviceScreen,
|
||||
iconUrl: deviceScreenIcon,
|
||||
},
|
||||
{
|
||||
description: 'SVG icon',
|
||||
size: '13k',
|
||||
filename: 'squoosh.svg',
|
||||
url: logo,
|
||||
iconUrl: logoIcon,
|
||||
},
|
||||
] as const;
|
||||
|
||||
const blobAnimImport =
|
||||
!__PRERENDER__ && matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
? undefined
|
||||
: import('./blob-anim');
|
||||
const installButtonSource = 'introInstallButton-Purple';
|
||||
const supportsClipboardAPI =
|
||||
!__PRERENDER__ && navigator.clipboard && navigator.clipboard.read;
|
||||
|
||||
async function getImageClipboardItem(
|
||||
items: ClipboardItem[],
|
||||
): Promise<undefined | Blob> {
|
||||
for (const item of items) {
|
||||
const type = item.types.find((type) => type.startsWith('image/'));
|
||||
if (type) return item.getType(type);
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onFile?: (file: File) => void;
|
||||
showSnack?: SnackBarElement['showSnackbar'];
|
||||
}
|
||||
interface State {
|
||||
fetchingDemoIndex?: number;
|
||||
beforeInstallEvent?: BeforeInstallPromptEvent;
|
||||
showBlobSVG: boolean;
|
||||
}
|
||||
|
||||
export default class Intro extends Component<Props, State> {
|
||||
state: State = {
|
||||
showBlobSVG: true,
|
||||
};
|
||||
private fileInput?: HTMLInputElement;
|
||||
private blobCanvas?: HTMLCanvasElement;
|
||||
private installingViaButton = false;
|
||||
|
||||
componentDidMount() {
|
||||
// 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);
|
||||
|
||||
if (blobAnimImport) {
|
||||
blobAnimImport.then((module) => {
|
||||
this.setState(
|
||||
{
|
||||
showBlobSVG: false,
|
||||
},
|
||||
() => module.startBlobAnim(this.blobCanvas!),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: remove this
|
||||
const demo = demos[3];
|
||||
fetch(demo.url)
|
||||
.then((r) => r.blob())
|
||||
.then((blob) =>
|
||||
this.props.onFile!(
|
||||
new File([blob], demo.filename, { type: blob.type }),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener(
|
||||
'beforeinstallprompt',
|
||||
this.onBeforeInstallPromptEvent,
|
||||
);
|
||||
window.removeEventListener('appinstalled', this.onAppInstalled);
|
||||
}
|
||||
|
||||
private onFileChange = (event: Event): void => {
|
||||
const fileInput = event.target as HTMLInputElement;
|
||||
const file = fileInput.files && fileInput.files[0];
|
||||
if (!file) return;
|
||||
this.fileInput!.value = '';
|
||||
this.props.onFile!(file);
|
||||
};
|
||||
|
||||
private onOpenClick = () => {
|
||||
this.fileInput!.click();
|
||||
};
|
||||
|
||||
private onDemoClick = async (index: number, event: Event) => {
|
||||
try {
|
||||
this.setState({ fetchingDemoIndex: index });
|
||||
const demo = demos[index];
|
||||
const blob = await fetch(demo.url).then((r) => r.blob());
|
||||
const file = new File([blob], demo.filename, { type: blob.type });
|
||||
this.props.onFile!(file);
|
||||
} catch (err) {
|
||||
this.setState({ fetchingDemoIndex: undefined });
|
||||
this.props.showSnack!("Couldn't fetch demo image");
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
private onInstallClick = async (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;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
private onPasteClick = async () => {
|
||||
let clipboardItems: ClipboardItem[];
|
||||
|
||||
try {
|
||||
clipboardItems = await navigator.clipboard.read();
|
||||
} catch (err) {
|
||||
this.props.showSnack!(`No permission to access clipboard`);
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = await getImageClipboardItem(clipboardItems);
|
||||
|
||||
if (!blob) {
|
||||
this.props.showSnack!(`No image found in the clipboard`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onFile!(new File([blob], 'image.unknown'));
|
||||
};
|
||||
|
||||
render(
|
||||
{}: Props,
|
||||
{ fetchingDemoIndex, beforeInstallEvent, showBlobSVG }: State,
|
||||
) {
|
||||
return (
|
||||
<div class={style.intro}>
|
||||
<input
|
||||
class={style.hide}
|
||||
ref={linkRef(this, 'fileInput')}
|
||||
type="file"
|
||||
onChange={this.onFileChange}
|
||||
/>
|
||||
<div class={style.main}>
|
||||
{!__PRERENDER__ && (
|
||||
<canvas
|
||||
ref={linkRef(this, 'blobCanvas')}
|
||||
class={style.blobCanvas}
|
||||
/>
|
||||
)}
|
||||
<h1 class={style.logoContainer}>
|
||||
<img
|
||||
class={style.logo}
|
||||
src={logoWithText}
|
||||
alt="Squoosh"
|
||||
width="539"
|
||||
height="162"
|
||||
/>
|
||||
</h1>
|
||||
<div class={style.loadImg}>
|
||||
{showBlobSVG && (
|
||||
<svg
|
||||
class={style.blobSvg}
|
||||
viewBox="-1.25 -1.25 2.5 2.5"
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
>
|
||||
{startBlobs.map((points) => (
|
||||
<path
|
||||
d={points
|
||||
.map((point, i) => {
|
||||
const nextI = i === points.length - 1 ? 0 : i + 1;
|
||||
let d = '';
|
||||
if (i === 0) {
|
||||
d += `M${point[2]} ${point[3]}`;
|
||||
}
|
||||
return (
|
||||
d +
|
||||
`C${point[4]} ${point[5]} ${points[nextI][0]} ${points[nextI][1]} ${points[nextI][2]} ${points[nextI][3]}`
|
||||
);
|
||||
})
|
||||
.join('')}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
)}
|
||||
<div
|
||||
class={style.loadImgContent}
|
||||
style={{ visibility: __PRERENDER__ ? 'hidden' : undefined }}
|
||||
>
|
||||
<button class={style.loadBtn} onClick={this.onOpenClick}>
|
||||
<svg viewBox="0 0 24 24" class={style.loadIcon}>
|
||||
<path d="M19 7v3h-2V7h-3V5h3V2h2v3h3v2h-3zm-3 4V8h-3V5H5a2 2 0 00-2 2v12c0 1.1.9 2 2 2h12a2 2 0 002-2v-8h-3zM5 19l3-4 2 3 3-4 4 5H5z" />
|
||||
</svg>
|
||||
</button>
|
||||
<div>
|
||||
<span class={style.dropText}>Drop </span>OR{' '}
|
||||
{supportsClipboardAPI ? (
|
||||
<button class={style.pasteBtn} onClick={this.onPasteClick}>
|
||||
Paste
|
||||
</button>
|
||||
) : (
|
||||
'Paste'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class={style.demosContainer}>
|
||||
<svg viewBox="0 0 1920 140" class={style.topWave}>
|
||||
<path
|
||||
d="M1920 0l-107 28c-106 29-320 85-533 93-213 7-427-36-640-50s-427 0-533 7L0 85v171h1920z"
|
||||
class={style.subWave}
|
||||
/>
|
||||
<path
|
||||
d="M0 129l64-26c64-27 192-81 320-75 128 5 256 69 384 64 128-6 256-80 384-91s256 43 384 70c128 26 256 26 320 26h64v96H0z"
|
||||
class={style.mainWave}
|
||||
/>
|
||||
</svg>
|
||||
<div class={style.contentPadding}>
|
||||
<p class={style.demoTitle}>
|
||||
Or <strong>try one</strong> of these:
|
||||
</p>
|
||||
<ul class={style.demos}>
|
||||
{demos.map((demo, i) => (
|
||||
<li>
|
||||
<button
|
||||
class="unbutton"
|
||||
onClick={(event) => this.onDemoClick(i, event)}
|
||||
>
|
||||
<div>
|
||||
<div class={style.demoIconContainer}>
|
||||
<img
|
||||
class={style.demoIcon}
|
||||
src={demo.iconUrl}
|
||||
alt={demo.description}
|
||||
/>
|
||||
{fetchingDemoIndex === i && (
|
||||
<div class={style.demoLoader}>
|
||||
<loading-spinner />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div class={style.demoSize}>{demo.size}</div>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class={style.footer}>
|
||||
<svg viewBox="0 0 1920 79" class={style.topWave}>
|
||||
<path
|
||||
d="M0 59l64-11c64-11 192-34 320-43s256-5 384 4 256 23 384 34 256 21 384 14 256-30 320-41l64-11v94H0z"
|
||||
class={style.footerWave}
|
||||
/>
|
||||
</svg>
|
||||
<div class={style.contentPadding}>
|
||||
<footer class={style.footerItems}>
|
||||
<a
|
||||
class={style.footerLink}
|
||||
href="https://github.com/GoogleChromeLabs/squoosh/blob/dev/README.md#privacy"
|
||||
>
|
||||
Privacy
|
||||
</a>
|
||||
<a
|
||||
class={style.footerLinkWithLogo}
|
||||
href="https://github.com/GoogleChromeLabs/squoosh"
|
||||
>
|
||||
<img src={githubLogo} alt="" width="10" height="10" />
|
||||
Source on Github
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
{beforeInstallEvent && (
|
||||
<button class={style.installBtn} onClick={this.onInstallClick}>
|
||||
Install
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
.intro {
|
||||
composes: abs-fill from global;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr max-content max-content;
|
||||
font-size: 1.2rem;
|
||||
color: var(--dim-text);
|
||||
}
|
||||
|
||||
.blob-canvas {
|
||||
composes: abs-fill from global;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: 541px;
|
||||
display: grid;
|
||||
grid-template-rows: max-content max-content;
|
||||
justify-items: center;
|
||||
position: relative;
|
||||
--blob-pink: var(--hot-pink);
|
||||
--center-blob-opacity: 0.3;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
min-height: 688px;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
margin: 5rem 0 1rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
transform: translate(-1%, 0);
|
||||
width: 189px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.load-img {
|
||||
position: relative;
|
||||
color: var(--white);
|
||||
font-style: italic;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.blob-svg {
|
||||
composes: abs-fill from global;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
fill: var(--blob-pink);
|
||||
|
||||
& path {
|
||||
opacity: var(--center-blob-opacity);
|
||||
}
|
||||
}
|
||||
|
||||
.load-img-content {
|
||||
position: relative;
|
||||
--size: 29rem;
|
||||
max-width: var(--size);
|
||||
width: 100vw;
|
||||
height: var(--size);
|
||||
display: grid;
|
||||
grid-template-rows: max-content max-content;
|
||||
justify-items: center;
|
||||
align-content: center;
|
||||
gap: 0.7rem;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
--size: 36rem;
|
||||
}
|
||||
}
|
||||
|
||||
.load-btn {
|
||||
composes: unbutton from global;
|
||||
}
|
||||
|
||||
.load-icon {
|
||||
--size: 5rem;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
fill: var(--white);
|
||||
transform: translate(4.3%, -1%);
|
||||
}
|
||||
|
||||
.paste-btn {
|
||||
composes: unbutton from global;
|
||||
text-decoration: underline;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.demos-container {
|
||||
position: relative;
|
||||
background: var(--deep-blue);
|
||||
padding-bottom: 5.2vw;
|
||||
}
|
||||
|
||||
.top-wave {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 100%;
|
||||
}
|
||||
|
||||
.main-wave {
|
||||
fill: var(--deep-blue);
|
||||
}
|
||||
|
||||
.sub-wave {
|
||||
fill: var(--light-blue);
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: relative;
|
||||
background: var(--light-gray);
|
||||
}
|
||||
|
||||
.footer-wave {
|
||||
fill: var(--light-gray);
|
||||
}
|
||||
|
||||
.content-padding {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.footer-items {
|
||||
display: grid;
|
||||
justify-content: end;
|
||||
grid-auto-columns: max-content;
|
||||
grid-auto-flow: column;
|
||||
align-items: center;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.footer-link-with-logo {
|
||||
composes: footer-link;
|
||||
display: grid;
|
||||
grid-template-columns: 1.8em max-content;
|
||||
align-items: center;
|
||||
gap: 0.6em;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.install-btn {
|
||||
composes: unbutton from global;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: var(--deep-blue);
|
||||
border-radius: 0.4em;
|
||||
color: var(--white);
|
||||
padding: 0.5em 1em;
|
||||
font-size: 1.6rem;
|
||||
animation: fade-in 600ms ease-in-out;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
color: var(--white);
|
||||
margin: 0;
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.demos {
|
||||
display: grid;
|
||||
gap: 3rem;
|
||||
justify-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
margin: 3rem auto;
|
||||
--demo-size: 80px;
|
||||
grid-template-columns: repeat(auto-fit, var(--demo-size));
|
||||
|
||||
@media (min-width: 740px) {
|
||||
--demo-size: 100px;
|
||||
gap: 6rem;
|
||||
}
|
||||
|
||||
& > li {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-size {
|
||||
background: var(--dim-blue);
|
||||
border-radius: 1000px;
|
||||
color: var(--white);
|
||||
width: max-content;
|
||||
padding: 0.5rem 1.2rem;
|
||||
margin: 0.7rem auto 0;
|
||||
}
|
||||
|
||||
.demo-icon-container {
|
||||
border-radius: var(--demo-size);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.demo-icon {
|
||||
width: var(--demo-size);
|
||||
height: var(--demo-size);
|
||||
display: block;
|
||||
}
|
||||
.demo-loader {
|
||||
composes: abs-fill from global;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
animation: fade-in 600ms ease-in-out;
|
||||
|
||||
& > loading-spinner {
|
||||
--color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
.drop-text {
|
||||
@media (max-width: 599px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||