Compare commits

..

2 Commits

Author SHA1 Message Date
Jake Archibald
dce76bb0f4 Remove useRandomMatrix 2020-12-17 10:10:47 +00:00
Jake Archibald
d6c178f9dc Tweaking wp2 options 2020-12-15 10:32:20 +00:00
41 changed files with 207 additions and 2457 deletions

BIN
codecs/wp2/dec/wp2_dec.wasm Normal file → Executable file

Binary file not shown.

Binary file not shown.

View File

@@ -17,7 +17,7 @@ struct WP2Options {
float sns;
int csp_type;
int error_diffusion;
bool use_random_matrix;
int tile_shape;
};
val encode(std::string image_in, int image_width, int image_height, WP2Options options) {
@@ -31,7 +31,7 @@ val encode(std::string image_in, int image_width, int image_height, WP2Options o
config.csp_type = static_cast<WP2::Csp>(options.csp_type);
config.sns = options.sns;
config.error_diffusion = options.error_diffusion;
config.use_random_matrix = options.use_random_matrix;
config.tile_shape = static_cast<WP2::TileShape>(options.tile_shape);
uint8_t* image_buffer = (uint8_t*)image_in.c_str();
WP2::ArgbBuffer src = WP2::ArgbBuffer();
@@ -62,7 +62,6 @@ EMSCRIPTEN_BINDINGS(my_module) {
.field("uv_mode", &WP2Options::uv_mode)
.field("csp_type", &WP2Options::csp_type)
.field("error_diffusion", &WP2Options::error_diffusion)
.field("use_random_matrix", &WP2Options::use_random_matrix)
.field("sns", &WP2Options::sns);
function("encode", &encode);

View File

@@ -7,7 +7,7 @@ export interface EncodeOptions {
uv_mode: UVMode;
csp_type: Csp;
error_diffusion: number;
use_random_matrix: boolean;
tile_shape: TileShape;
}
export const enum UVMode {
@@ -24,6 +24,14 @@ export const enum Csp {
kYIQ,
}
export const enum TileShape {
Square128,
Square256,
Square512,
Wide,
Auto,
}
export interface WP2Module extends EmscriptenWasm.Module {
encode(
data: BufferSource,

View File

@@ -35,12 +35,12 @@ toWireType:function(m,h){return h?d:e},argPackAdvance:8,readValueFromPointer:fun
toWireType:function(d,e){if("number"!==typeof e&&"boolean"!==typeof e)throw new TypeError('Cannot convert "'+Ua(e)+'" to '+this.name);return e},argPackAdvance:8,readValueFromPointer:Va(b,c),K:null})},m:function(a,b,c,d,e,g){var m=Za(b,c);a=U(a);e=Y(d,e);Ya(a,function(){db("Cannot call "+a+" due to unbound types",m)},b-1);Na([],m,function(h){var k=a,l=a;h=[h[0],null].concat(h.slice(1));var n=e,q=h.length;2>q&&W("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var x=
null!==h[1]&&!1,y=!1,p=1;p<h.length;++p)if(null!==h[p]&&void 0===h[p].K){y=!0;break}var ha="void"!==h[0].name,z="",F="";for(p=0;p<q-2;++p)z+=(0!==p?", ":"")+"arg"+p,F+=(0!==p?", ":"")+"arg"+p+"Wired";l="return function "+Ja(l)+"("+z+") {\nif (arguments.length !== "+(q-2)+") {\nthrowBindingError('function "+l+" called with ' + arguments.length + ' arguments, expected "+(q-2)+" args!');\n}\n";y&&(l+="var destructors = [];\n");var V=y?"destructors":"null";z="throwBindingError invoker fn runDestructors retType classParam".split(" ");
n=[W,n,g,Ga,h[0],h[1]];x&&(l+="var thisWired = classParam.toWireType("+V+", this);\n");for(p=0;p<q-2;++p)l+="var arg"+p+"Wired = argType"+p+".toWireType("+V+", arg"+p+"); // "+h[p+2].name+"\n",z.push("argType"+p),n.push(h[p+2]);x&&(F="thisWired"+(0<F.length?", ":"")+F);l+=(ha?"var rv = ":"")+"invoker(fn"+(0<F.length?", ":"")+F+");\n";if(y)l+="runDestructors(destructors);\n";else for(p=x?1:2;p<h.length;++p)q=1===p?"thisWired":"arg"+(p-2)+"Wired",null!==h[p].K&&(l+=q+"_dtor("+q+"); // "+h[p].name+"\n",
z.push(q+"_dtor"),n.push(h[p].K));ha&&(l+="var ret = retType.fromWireType(rv);\nreturn ret;\n");z.push(l+"}\n");h=Wa(z).apply(null,n);p=b-1;if(!f.hasOwnProperty(k))throw new Ma("Replacing nonexistant public symbol");void 0!==f[k].J&&void 0!==p?f[k].J[p]=h:(f[k]=h,f[k].R=p);return[]})},d:function(a,b,c,d,e){function g(l){return l}b=U(b);-1===e&&(e=4294967295);var m=Oa(c);if(0===d){var h=32-8*c;g=function(l){return l<<h>>>h}}var k=-1!=b.indexOf("unsigned");T(a,{name:b,fromWireType:g,toWireType:function(l,
z.push(q+"_dtor"),n.push(h[p].K));ha&&(l+="var ret = retType.fromWireType(rv);\nreturn ret;\n");z.push(l+"}\n");h=Wa(z).apply(null,n);p=b-1;if(!f.hasOwnProperty(k))throw new Ma("Replacing nonexistant public symbol");void 0!==f[k].J&&void 0!==p?f[k].J[p]=h:(f[k]=h,f[k].R=p);return[]})},c:function(a,b,c,d,e){function g(l){return l}b=U(b);-1===e&&(e=4294967295);var m=Oa(c);if(0===d){var h=32-8*c;g=function(l){return l<<h>>>h}}var k=-1!=b.indexOf("unsigned");T(a,{name:b,fromWireType:g,toWireType:function(l,
n){if("number"!==typeof n&&"boolean"!==typeof n)throw new TypeError('Cannot convert "'+Ua(n)+'" to '+this.name);if(n<d||n>e)throw new TypeError('Passing a number "'+Ua(n)+'" from JS side to C/C++ side to an argument of type "'+b+'", which is outside the valid range ['+d+", "+e+"]!");return k?n>>>0:n|0},argPackAdvance:8,readValueFromPointer:eb(b,m,0!==d),K:null})},b:function(a,b,c){function d(g){g>>=2;var m=K;return new e(I,m[g+1],m[g])}var e=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,
Uint32Array,Float32Array,Float64Array][b];c=U(c);T(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{X:!0})},k:function(a,b){b=U(b);var c="std::string"===b;T(a,{name:b,fromWireType:function(d){var e=K[d>>2];if(c)for(var g=d+4,m=0;m<=e;++m){var h=d+4+m;if(m==e||0==D[h]){g=C(g,h-g);if(void 0===k)var k=g;else k+=String.fromCharCode(0),k+=g;g=h+1}}else{k=Array(e);for(m=0;m<e;++m)k[m]=String.fromCharCode(D[d+4+m]);k=k.join("")}Z(d);return k},toWireType:function(d,e){e instanceof ArrayBuffer&&
(e=new Uint8Array(e));var g="string"===typeof e;g||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int8Array||W("Cannot pass non-string to std::string");var m=(c&&g?function(){for(var l=0,n=0;n<e.length;++n){var q=e.charCodeAt(n);55296<=q&&57343>=q&&(q=65536+((q&1023)<<10)|e.charCodeAt(++n)&1023);127>=q?++l:l=2047>=q?l+2:65535>=q?l+3:l+4}return l}:function(){return e.length})(),h=nb(4+m+1);K[h>>2]=m;if(c&&g)ia(e,h+4,m+1);else if(g)for(g=0;g<m;++g){var k=e.charCodeAt(g);255<k&&
(Z(h),W("String has UTF-16 code units that do not fit in 8 bits"));D[h+4+g]=k}else for(g=0;g<m;++g)D[h+4+g]=e[g];null!==d&&d.push(Z,h);return h},argPackAdvance:8,readValueFromPointer:Ha,K:function(d){Z(d)}})},f:function(a,b,c){c=U(c);if(2===b){var d=ka;var e=la;var g=ma;var m=function(){return E};var h=1}else 4===b&&(d=na,e=oa,g=pa,m=function(){return K},h=2);T(a,{name:c,fromWireType:function(k){for(var l=K[k>>2],n=m(),q,x=k+4,y=0;y<=l;++y){var p=k+4+y*b;if(y==l||0==n[p>>h])x=d(x,p-x),void 0===q?
q=x:(q+=String.fromCharCode(0),q+=x),x=p+b}Z(k);return q},toWireType:function(k,l){"string"!==typeof l&&W("Cannot pass non-string to C++ string type "+c);var n=g(l),q=nb(4+n+b);K[q>>2]=n>>h;e(l,q+4,n+b);null!==k&&k.push(Z,q);return q},argPackAdvance:8,readValueFromPointer:Ha,K:function(k){Z(k)}})},o:function(a,b,c,d,e,g){Fa[a]={name:U(b),Z:Y(c,d),$:Y(e,g),O:[]}},c:function(a,b,c,d,e,g,m,h,k,l){Fa[a].O.push({S:U(b),W:c,U:Y(d,e),V:g,ga:m,fa:Y(h,k),ha:l})},x:function(a,b){b=U(b);T(a,{ia:!0,name:b,argPackAdvance:0,
q=x:(q+=String.fromCharCode(0),q+=x),x=p+b}Z(k);return q},toWireType:function(k,l){"string"!==typeof l&&W("Cannot pass non-string to C++ string type "+c);var n=g(l),q=nb(4+n+b);K[q>>2]=n>>h;e(l,q+4,n+b);null!==k&&k.push(Z,q);return q},argPackAdvance:8,readValueFromPointer:Ha,K:function(k){Z(k)}})},o:function(a,b,c,d,e,g){Fa[a]={name:U(b),Z:Y(c,d),$:Y(e,g),O:[]}},d:function(a,b,c,d,e,g,m,h,k,l){Fa[a].O.push({S:U(b),W:c,U:Y(d,e),V:g,ga:m,fa:Y(h,k),ha:l})},x:function(a,b){b=U(b);T(a,{ia:!0,name:b,argPackAdvance:0,
fromWireType:function(){},toWireType:function(){}})},i:Sa,A:function(a){if(0===a)return Ta(gb());var b=fb[a];a=void 0===b?U(a):b;return Ta(gb()[a])},l:function(a){4<a&&(X[a].N+=1)},p:function(a,b,c,d){a||W("Cannot use deleted val. handle = "+a);a=X[a].value;var e=ib[b];if(!e){e="";for(var g=0;g<b;++g)e+=(0!==g?", ":"")+"arg"+g;var m="return function emval_allocator_"+b+"(constructor, argTypes, args) {\n";for(g=0;g<b;++g)m+="var argType"+g+" = requireRegisteredType(Module['HEAP32'][(argTypes >>> 2) + "+
g+'], "parameter '+g+'");\nvar arg'+g+" = argType"+g+".readValueFromPointer(args);\nargs += argType"+g+"['argPackAdvance'];\n";e=(new Function("requireRegisteredType","Module","__emval_register",m+("var obj = new constructor("+e+");\nreturn __emval_register(obj);\n}\n")))(hb,f,Ta);ib[b]=e}return e(a,c,d)},g:function(){A()},t:function(a,b,c){D.copyWithin(a,b,b+c)},e:function(a){a>>>=0;var b=D.length;if(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{B.grow(Math.min(2147483648,d)-I.byteLength+65535>>>16);sa(B.buffer);var e=1;break a}catch(g){}e=void 0}if(e)return!0}return!1},u:function(){return 0},r:function(){},h:function(a,b,c,d){for(var e=0,g=0;g<c;g++){for(var m=H[b+8*g>>2],h=H[b+(8*g+4)>>2],k=0;k<h;k++){var l=D[m+k],n=jb[a];if(0===l||10===l){for(l=0;n[l]&&!(NaN<=l);)++l;l=fa.decode(n.subarray?n.subarray(0,l):new Uint8Array(n.slice(0,l)));(1===a?da:v)(l);n.length=0}else n.push(l)}e+=h}H[d>>2]=e;return 0},

BIN
codecs/wp2/enc/wp2_enc.wasm Normal file → Executable file

Binary file not shown.

View File

@@ -17,7 +17,7 @@ function Ba(a){return 2*a.length}function Ca(a,b){for(var c=0,d="";!(c>=b/4);){v
function Ea(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}var m,aa,ca,da,fa,ha,ia,ka,ma;function p(a){m=a;A.HEAP8=aa=new Int8Array(a);A.HEAP16=da=new Int16Array(a);A.HEAP32=ha=new Int32Array(a);A.HEAPU8=ca=new Uint8Array(a);A.HEAPU16=fa=new Uint16Array(a);A.HEAPU32=ia=new Uint32Array(a);A.HEAPF32=ka=new Float32Array(a);A.HEAPF64=ma=new Float64Array(a)}var Fa=A.INITIAL_MEMORY||16777216;
if(D)k=A.wasmMemory,m=A.buffer;else if(A.wasmMemory)k=A.wasmMemory;else if(k=new WebAssembly.Memory({initial:Fa/65536,maximum:32768,shared:!0}),!(k.buffer instanceof SharedArrayBuffer))throw H("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");k&&(m=k.buffer);Fa=m.byteLength;p(m);var L,Ga=[],Ha=[],Ia=[],Ja=[];
function Ka(){var a=A.preRun.shift();Ga.unshift(a)}var N=0,La=null,O=null;A.preloadedImages={};A.preloadedAudios={};function J(a){if(A.onAbort)A.onAbort(a);D&&console.error("Pthread aborting at "+Error().stack);H(a);ua=!0;a=new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");oa(a);throw a;}function Ma(){var a=P;return String.prototype.startsWith?a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}var P="wp2_enc_mt.wasm";
Ma()||(P=qa(P));function Na(){try{if(I)return new Uint8Array(I);if(ra)return ra(P);throw"both async and sync fetching of the wasm failed";}catch(a){J(a)}}function Oa(){return I||"function"!==typeof fetch?Promise.resolve().then(Na):fetch(P,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+P+"'";return a.arrayBuffer()}).catch(function(){return Na()})}var Qa={124965:function(a,b){setTimeout(function(){Pa(a,b)},0)},125043:function(){throw"Canceled!";}};
Ma()||(P=qa(P));function Na(){try{if(I)return new Uint8Array(I);if(ra)return ra(P);throw"both async and sync fetching of the wasm failed";}catch(a){J(a)}}function Oa(){return I||"function"!==typeof fetch?Promise.resolve().then(Na):fetch(P,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+P+"'";return a.arrayBuffer()}).catch(function(){return Na()})}var Qa={124949:function(a,b){setTimeout(function(){Pa(a,b)},0)},125027:function(){throw"Canceled!";}};
function Ra(a){for(;0<a.length;){var b=a.shift();if("function"==typeof b)b(A);else{var c=b.mb;"number"===typeof c?void 0===b.Ka?L.get(c)():L.get(c)(b.Ka):c(void 0===b.Ka?null:b.Ka)}}}function Sa(a,b,c){var d;-1!=a.indexOf("j")?d=c&&c.length?A["dynCall_"+a].apply(null,[b].concat(c)):A["dynCall_"+a].call(null,b):d=L.get(b).apply(null,c);return d}A.dynCall=Sa;var Q=0,Ta=0,Ua=0;function Va(a,b,c){Q=a|0;Ua=b|0;Ta=c|0}A.registerPthreadPtr=Va;
function Wa(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(y(),R.bb>>2),d=0;if(c==a&&Atomics.compareExchange(y(),R.bb>>2,c,0)==c&&(--b,d=1,0>=b))return 1;a=Atomics.notify(y(),a>>2,b);if(0<=a)return a+d;throw"Atomics.notify returned an unexpected value "+a;}A._emscripten_futex_wake=Wa;
function Xa(a){if(D)throw"Internal Error! cleanupThread() can only ever be called from main application thread!";if(!a)throw"Internal Error! Null pthread_ptr in cleanupThread!";y()[a+12>>2]=0;(a=R.Ea[a])&&R.Qa(a.worker)}
@@ -59,12 +59,12 @@ qb(c);b=X(b);W(a,{name:b,fromWireType:function(l){return!!l},toWireType:function
b,c){c=qb(c);b=X(b);W(a,{name:b,fromWireType:function(d){return d},toWireType:function(d,f){if("number"!==typeof f&&"boolean"!==typeof f)throw new TypeError('Cannot convert "'+wb(f)+'" to '+this.name);return f},argPackAdvance:8,readValueFromPointer:xb(b,c),Fa:null})},w:function(a,b,c,d,f,g){var l=Bb(b,c);a=X(a);f=Db(d,f);Ab(a,function(){Hb("Cannot call "+a+" due to unbound types",l)},b-1);pb([],l,function(h){var n=a,r=a;h=[h[0],null].concat(h.slice(1));var q=f,v=h.length;2>v&&Y("argTypes array size mismatch! Must at least get return value and 'this' types!");
for(var x=null!==h[1]&&!1,E=!1,t=1;t<h.length;++t)if(null!==h[t]&&void 0===h[t].Fa){E=!0;break}var pa="void"!==h[0].name,G="",M="";for(t=0;t<v-2;++t)G+=(0!==t?", ":"")+"arg"+t,M+=(0!==t?", ":"")+"arg"+t+"Wired";r="return function "+lb(r)+"("+G+") {\nif (arguments.length !== "+(v-2)+") {\nthrowBindingError('function "+r+" called with ' + arguments.length + ' arguments, expected "+(v-2)+" args!');\n}\n";E&&(r+="var destructors = [];\n");var ba=E?"destructors":"null";G="throwBindingError invoker fn runDestructors retType classParam".split(" ");
q=[Y,q,g,ib,h[0],h[1]];x&&(r+="var thisWired = classParam.toWireType("+ba+", this);\n");for(t=0;t<v-2;++t)r+="var arg"+t+"Wired = argType"+t+".toWireType("+ba+", arg"+t+"); // "+h[t+2].name+"\n",G.push("argType"+t),q.push(h[t+2]);x&&(M="thisWired"+(0<M.length?", ":"")+M);r+=(pa?"var rv = ":"")+"invoker(fn"+(0<M.length?", ":"")+M+");\n";if(E)r+="runDestructors(destructors);\n";else for(t=x?1:2;t<h.length;++t)v=1===t?"thisWired":"arg"+(t-2)+"Wired",null!==h[t].Fa&&(r+=v+"_dtor("+v+"); // "+h[t].name+
"\n",G.push(v+"_dtor"),q.push(h[t].Fa));pa&&(r+="var ret = retType.fromWireType(rv);\nreturn ret;\n");G.push(r+"}\n");h=yb(G).apply(null,q);t=b-1;if(!A.hasOwnProperty(n))throw new ob("Replacing nonexistant public symbol");void 0!==A[n].Da&&void 0!==t?A[n].Da[t]=h:(A[n]=h,A[n].jb=t);return[]})},j:function(a,b,c,d,f){function g(r){return r}b=X(b);-1===f&&(f=4294967295);var l=qb(c);if(0===d){var h=32-8*c;g=function(r){return r<<h>>>h}}var n=-1!=b.indexOf("unsigned");W(a,{name:b,fromWireType:g,toWireType:function(r,
"\n",G.push(v+"_dtor"),q.push(h[t].Fa));pa&&(r+="var ret = retType.fromWireType(rv);\nreturn ret;\n");G.push(r+"}\n");h=yb(G).apply(null,q);t=b-1;if(!A.hasOwnProperty(n))throw new ob("Replacing nonexistant public symbol");void 0!==A[n].Da&&void 0!==t?A[n].Da[t]=h:(A[n]=h,A[n].jb=t);return[]})},i:function(a,b,c,d,f){function g(r){return r}b=X(b);-1===f&&(f=4294967295);var l=qb(c);if(0===d){var h=32-8*c;g=function(r){return r<<h>>>h}}var n=-1!=b.indexOf("unsigned");W(a,{name:b,fromWireType:g,toWireType:function(r,
q){if("number"!==typeof q&&"boolean"!==typeof q)throw new TypeError('Cannot convert "'+wb(q)+'" to '+this.name);if(q<d||q>f)throw new TypeError('Passing a number "'+wb(q)+'" from JS side to C/C++ side to an argument of type "'+b+'", which is outside the valid range ['+d+", "+f+"]!");return n?q>>>0:q|0},argPackAdvance:8,readValueFromPointer:Ib(b,l,0!==d),Fa:null})},g:function(a,b,c){function d(g){g>>=2;var l=z();return new f(m,l[g+1],l[g])}var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,
Uint32Array,Float32Array,Float64Array][b];c=X(c);W(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{rb:!0})},u:function(a,b){b=X(b);var c="std::string"===b;W(a,{name:b,fromWireType:function(d){var f=z()[d>>2];if(c)for(var g=d+4,l=0;l<=f;++l){var h=d+4+l;if(l==f||0==u()[h]){g=K(g,h-g);if(void 0===n)var n=g;else n+=String.fromCharCode(0),n+=g;g=h+1}}else{n=Array(f);for(l=0;l<f;++l)n[l]=String.fromCharCode(u()[d+4+l]);n=n.join("")}T(d);return n},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||Y("Cannot pass non-string to std::string");var l=(c&&g?function(){return ya(f)}:function(){return f.length})(),h=S(4+l+1);z()[h>>2]=l;if(c&&g)xa(f,h+4,l+1);else if(g)for(g=0;g<l;++g){var n=f.charCodeAt(g);255<n&&(T(h),Y("String has UTF-16 code units that do not fit in 8 bits"));u()[h+4+g]=n}else for(g=0;g<l;++g)u()[h+4+g]=f[g];null!==d&&d.push(T,h);return h},
argPackAdvance:8,readValueFromPointer:jb,Fa:function(d){T(d)}})},o:function(a,b,c){c=X(c);if(2===b){var d=za;var f=Aa;var g=Ba;var l=function(){return ea()};var h=1}else 4===b&&(d=Ca,f=Da,g=Ea,l=function(){return z()},h=2);W(a,{name:c,fromWireType:function(n){for(var r=z()[n>>2],q=l(),v,x=n+4,E=0;E<=r;++E){var t=n+4+E*b;if(E==r||0==q[t>>h])x=d(x,t-x),void 0===v?v=x:(v+=String.fromCharCode(0),v+=x),x=t+b}T(n);return v},toWireType:function(n,r){"string"!==typeof r&&Y("Cannot pass non-string to C++ string type "+
c);var q=g(r),v=S(4+q+b);z()[v>>2]=q>>h;f(r,v+4,q+b);null!==n&&n.push(T,v);return v},argPackAdvance:8,readValueFromPointer:jb,Fa:function(n){T(n)}})},y:function(a,b,c,d,f,g){hb[a]={name:X(b),zb:Db(c,d),Ab:Db(f,g),Za:[]}},i:function(a,b,c,d,f,g,l,h,n,r){hb[a].Za.push({lb:X(b),qb:c,ob:Db(d,f),pb:g,Jb:l,Ib:Db(h,n),Kb:r})},O:function(a,b){b=X(b);W(a,{Vb:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},I:function(a,b){if(a==b)postMessage({cmd:"processQueuedMainThreadWork"});
c);var q=g(r),v=S(4+q+b);z()[v>>2]=q>>h;f(r,v+4,q+b);null!==n&&n.push(T,v);return v},argPackAdvance:8,readValueFromPointer:jb,Fa:function(n){T(n)}})},y:function(a,b,c,d,f,g){hb[a]={name:X(b),zb:Db(c,d),Ab:Db(f,g),Za:[]}},j:function(a,b,c,d,f,g,l,h,n,r){hb[a].Za.push({lb:X(b),qb:c,ob:Db(d,f),pb:g,Jb:l,Ib:Db(h,n),Kb:r})},O:function(a,b){b=X(b);W(a,{Vb:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},I:function(a,b){if(a==b)postMessage({cmd:"processQueuedMainThreadWork"});
else if(D)postMessage({targetThread:a,cmd:"processThreadQueue"});else{a=(a=R.Ea[a])&&a.worker;if(!a)return;a.postMessage({cmd:"processThreadQueue"})}return 1},q:ub,L:function(a){if(0===a)return vb(Kb());var b=Jb[a];a=void 0===b?X(a):b;return vb(Kb()[a])},S:function(a){4<a&&(Z[a].Ua+=1)},z:function(a,b,c,d){a||Y("Cannot use deleted val. handle = "+a);a=Z[a].value;var f=Mb[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")))(Lb,A,vb);Mb[b]=f}return f(a,c,d)},r:function(){J()},l:function(a,b,c){Tb.length=0;var d;for(c>>=2;d=u()[b++];)(d=105>d)&&c&1&&c++,
Tb.push(d?la()[c++>>1]:y()[c]),++c;return Qa[a].apply(null,Tb)},J:function(){},p:function(){},e:Nb,f:Wa,b:db,n:function(){return Ua|0},m:function(){return Ta|0},D:function(a,b,c){u().copyWithin(a,b,b+c)},T:function(){return navigator.hardwareConcurrency},F:function(a,b,c){Sb.length=b;c>>=3;for(var d=0;d<b;d++)Sb[d]=la()[c+d];return(0>a?Qa[-a-1]:nc[a]).apply(null,Sb)},k:function(a){a>>>=0;var b=u().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);
@@ -84,7 +84,7 @@ A._emscripten_sync_run_in_main_thread=function(){return(A._emscripten_sync_run_i
A._emscripten_sync_run_in_main_thread_2=function(){return(A._emscripten_sync_run_in_main_thread_2=A.asm.pa).apply(null,arguments)};A._emscripten_sync_run_in_main_thread_xprintf_varargs=function(){return(A._emscripten_sync_run_in_main_thread_xprintf_varargs=A.asm.qa).apply(null,arguments)};A._emscripten_sync_run_in_main_thread_3=function(){return(A._emscripten_sync_run_in_main_thread_3=A.asm.ra).apply(null,arguments)};
var pc=A._emscripten_sync_run_in_main_thread_4=function(){return(pc=A._emscripten_sync_run_in_main_thread_4=A.asm.sa).apply(null,arguments)};A._emscripten_sync_run_in_main_thread_5=function(){return(A._emscripten_sync_run_in_main_thread_5=A.asm.ta).apply(null,arguments)};A._emscripten_sync_run_in_main_thread_6=function(){return(A._emscripten_sync_run_in_main_thread_6=A.asm.ua).apply(null,arguments)};
A._emscripten_sync_run_in_main_thread_7=function(){return(A._emscripten_sync_run_in_main_thread_7=A.asm.va).apply(null,arguments)};var Rb=A._emscripten_run_in_main_runtime_thread_js=function(){return(Rb=A._emscripten_run_in_main_runtime_thread_js=A.asm.wa).apply(null,arguments)},Xb=A.__emscripten_call_on_thread=function(){return(Xb=A.__emscripten_call_on_thread=A.asm.xa).apply(null,arguments)};A._emscripten_tls_init=function(){return(A._emscripten_tls_init=A.asm.ya).apply(null,arguments)};
A.dynCall_jiji=function(){return(A.dynCall_jiji=A.asm.za).apply(null,arguments)};var Za=A._main_thread_futex=139708;A.PThread=R;A.PThread=R;A._pthread_self=jc;A.wasmMemory=k;A.ExitStatus=sc;var tc;function sc(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}O=function uc(){tc||vc();tc||(O=uc)};
A.dynCall_jiji=function(){return(A.dynCall_jiji=A.asm.za).apply(null,arguments)};var Za=A._main_thread_futex=139692;A.PThread=R;A.PThread=R;A._pthread_self=jc;A.wasmMemory=k;A.ExitStatus=sc;var tc;function sc(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}O=function uc(){tc||vc();tc||(O=uc)};
function vc(){function a(){if(!tc&&(tc=!0,A.calledRun=!0,!ua)){Ra(Ha);D||Ra(Ia);na(A);if(A.onRuntimeInitialized)A.onRuntimeInitialized();if(!D){if(A.postRun)for("function"==typeof A.postRun&&(A.postRun=[A.postRun]);A.postRun.length;){var b=A.postRun.shift();Ja.unshift(b)}Ra(Ja)}}}if(!(0<N)){if(!D){if(A.preRun)for("function"==typeof A.preRun&&(A.preRun=[A.preRun]);A.preRun.length;)Ka();Ra(Ga)}0<N||(A.setStatus?(A.setStatus("Running..."),setTimeout(function(){setTimeout(function(){A.setStatus("")},
1);a()},1)):a())}}A.run=vc;if(A.preInit)for("function"==typeof A.preInit&&(A.preInit=[A.preInit]);0<A.preInit.length;)A.preInit.pop()();D||(noExitRuntime=!0);D?R.wb():vc();

BIN
codecs/wp2/enc/wp2_enc_mt.wasm Normal file → Executable file

Binary file not shown.

View File

@@ -17,7 +17,7 @@ function Ba(a){return 2*a.length}function Ca(a,b){for(var c=0,d="";!(c>=b/4);){v
function Ea(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}var m,aa,ca,da,fa,ha,ia,ka,ma;function p(a){m=a;A.HEAP8=aa=new Int8Array(a);A.HEAP16=da=new Int16Array(a);A.HEAP32=ha=new Int32Array(a);A.HEAPU8=ca=new Uint8Array(a);A.HEAPU16=fa=new Uint16Array(a);A.HEAPU32=ia=new Uint32Array(a);A.HEAPF32=ka=new Float32Array(a);A.HEAPF64=ma=new Float64Array(a)}var Fa=A.INITIAL_MEMORY||16777216;
if(D)k=A.wasmMemory,m=A.buffer;else if(A.wasmMemory)k=A.wasmMemory;else if(k=new WebAssembly.Memory({initial:Fa/65536,maximum:32768,shared:!0}),!(k.buffer instanceof SharedArrayBuffer))throw H("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");k&&(m=k.buffer);Fa=m.byteLength;p(m);var L,Ga=[],Ha=[],Ia=[],Ja=[];
function Ka(){var a=A.preRun.shift();Ga.unshift(a)}var N=0,La=null,O=null;A.preloadedImages={};A.preloadedAudios={};function J(a){if(A.onAbort)A.onAbort(a);D&&console.error("Pthread aborting at "+Error().stack);H(a);ua=!0;a=new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");oa(a);throw a;}function Ma(){var a=P;return String.prototype.startsWith?a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}var P="wp2_enc_mt_simd.wasm";
Ma()||(P=qa(P));function Na(){try{if(I)return new Uint8Array(I);if(ra)return ra(P);throw"both async and sync fetching of the wasm failed";}catch(a){J(a)}}function Oa(){return I||"function"!==typeof fetch?Promise.resolve().then(Na):fetch(P,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+P+"'";return a.arrayBuffer()}).catch(function(){return Na()})}var Qa={125045:function(a,b){setTimeout(function(){Pa(a,b)},0)},125123:function(){throw"Canceled!";}};
Ma()||(P=qa(P));function Na(){try{if(I)return new Uint8Array(I);if(ra)return ra(P);throw"both async and sync fetching of the wasm failed";}catch(a){J(a)}}function Oa(){return I||"function"!==typeof fetch?Promise.resolve().then(Na):fetch(P,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+P+"'";return a.arrayBuffer()}).catch(function(){return Na()})}var Qa={125029:function(a,b){setTimeout(function(){Pa(a,b)},0)},125107:function(){throw"Canceled!";}};
function Ra(a){for(;0<a.length;){var b=a.shift();if("function"==typeof b)b(A);else{var c=b.ob;"number"===typeof c?void 0===b.Ma?L.get(c)():L.get(c)(b.Ma):c(void 0===b.Ma?null:b.Ma)}}}function Sa(a,b,c){var d;-1!=a.indexOf("j")?d=c&&c.length?A["dynCall_"+a].apply(null,[b].concat(c)):A["dynCall_"+a].call(null,b):d=L.get(b).apply(null,c);return d}A.dynCall=Sa;var Q=0,Ta=0,Ua=0;function Va(a,b,c){Q=a|0;Ua=b|0;Ta=c|0}A.registerPthreadPtr=Va;
function Wa(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(y(),R.eb>>2),d=0;if(c==a&&Atomics.compareExchange(y(),R.eb>>2,c,0)==c&&(--b,d=1,0>=b))return 1;a=Atomics.notify(y(),a>>2,b);if(0<=a)return a+d;throw"Atomics.notify returned an unexpected value "+a;}A._emscripten_futex_wake=Wa;
function Xa(a){if(D)throw"Internal Error! cleanupThread() can only ever be called from main application thread!";if(!a)throw"Internal Error! Null pthread_ptr in cleanupThread!";y()[a+12>>2]=0;(a=R.Ga[a])&&R.Sa(a.worker)}
@@ -60,12 +60,12 @@ qb(c);b=X(b);W(a,{name:b,fromWireType:function(l){return!!l},toWireType:function
b,c){c=qb(c);b=X(b);W(a,{name:b,fromWireType:function(d){return d},toWireType:function(d,f){if("number"!==typeof f&&"boolean"!==typeof f)throw new TypeError('Cannot convert "'+wb(f)+'" to '+this.name);return f},argPackAdvance:8,readValueFromPointer:xb(b,c),Ha:null})},x:function(a,b,c,d,f,g){var l=Bb(b,c);a=X(a);f=Db(d,f);Ab(a,function(){Hb("Cannot call "+a+" due to unbound types",l)},b-1);pb([],l,function(h){var n=a,r=a;h=[h[0],null].concat(h.slice(1));var q=f,v=h.length;2>v&&Y("argTypes array size mismatch! Must at least get return value and 'this' types!");
for(var x=null!==h[1]&&!1,E=!1,t=1;t<h.length;++t)if(null!==h[t]&&void 0===h[t].Ha){E=!0;break}var pa="void"!==h[0].name,G="",M="";for(t=0;t<v-2;++t)G+=(0!==t?", ":"")+"arg"+t,M+=(0!==t?", ":"")+"arg"+t+"Wired";r="return function "+lb(r)+"("+G+") {\nif (arguments.length !== "+(v-2)+") {\nthrowBindingError('function "+r+" called with ' + arguments.length + ' arguments, expected "+(v-2)+" args!');\n}\n";E&&(r+="var destructors = [];\n");var ba=E?"destructors":"null";G="throwBindingError invoker fn runDestructors retType classParam".split(" ");
q=[Y,q,g,ib,h[0],h[1]];x&&(r+="var thisWired = classParam.toWireType("+ba+", this);\n");for(t=0;t<v-2;++t)r+="var arg"+t+"Wired = argType"+t+".toWireType("+ba+", arg"+t+"); // "+h[t+2].name+"\n",G.push("argType"+t),q.push(h[t+2]);x&&(M="thisWired"+(0<M.length?", ":"")+M);r+=(pa?"var rv = ":"")+"invoker(fn"+(0<M.length?", ":"")+M+");\n";if(E)r+="runDestructors(destructors);\n";else for(t=x?1:2;t<h.length;++t)v=1===t?"thisWired":"arg"+(t-2)+"Wired",null!==h[t].Ha&&(r+=v+"_dtor("+v+"); // "+h[t].name+
"\n",G.push(v+"_dtor"),q.push(h[t].Ha));pa&&(r+="var ret = retType.fromWireType(rv);\nreturn ret;\n");G.push(r+"}\n");h=yb(G).apply(null,q);t=b-1;if(!A.hasOwnProperty(n))throw new ob("Replacing nonexistant public symbol");void 0!==A[n].Fa&&void 0!==t?A[n].Fa[t]=h:(A[n]=h,A[n].lb=t);return[]})},j:function(a,b,c,d,f){function g(r){return r}b=X(b);-1===f&&(f=4294967295);var l=qb(c);if(0===d){var h=32-8*c;g=function(r){return r<<h>>>h}}var n=-1!=b.indexOf("unsigned");W(a,{name:b,fromWireType:g,toWireType:function(r,
"\n",G.push(v+"_dtor"),q.push(h[t].Ha));pa&&(r+="var ret = retType.fromWireType(rv);\nreturn ret;\n");G.push(r+"}\n");h=yb(G).apply(null,q);t=b-1;if(!A.hasOwnProperty(n))throw new ob("Replacing nonexistant public symbol");void 0!==A[n].Fa&&void 0!==t?A[n].Fa[t]=h:(A[n]=h,A[n].lb=t);return[]})},i:function(a,b,c,d,f){function g(r){return r}b=X(b);-1===f&&(f=4294967295);var l=qb(c);if(0===d){var h=32-8*c;g=function(r){return r<<h>>>h}}var n=-1!=b.indexOf("unsigned");W(a,{name:b,fromWireType:g,toWireType:function(r,
q){if("number"!==typeof q&&"boolean"!==typeof q)throw new TypeError('Cannot convert "'+wb(q)+'" to '+this.name);if(q<d||q>f)throw new TypeError('Passing a number "'+wb(q)+'" from JS side to C/C++ side to an argument of type "'+b+'", which is outside the valid range ['+d+", "+f+"]!");return n?q>>>0:q|0},argPackAdvance:8,readValueFromPointer:Ib(b,l,0!==d),Ha:null})},g:function(a,b,c){function d(g){g>>=2;var l=z();return new f(m,l[g+1],l[g])}var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,
Uint32Array,Float32Array,Float64Array][b];c=X(c);W(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{tb:!0})},v:function(a,b){b=X(b);var c="std::string"===b;W(a,{name:b,fromWireType:function(d){var f=z()[d>>2];if(c)for(var g=d+4,l=0;l<=f;++l){var h=d+4+l;if(l==f||0==u()[h]){g=K(g,h-g);if(void 0===n)var n=g;else n+=String.fromCharCode(0),n+=g;g=h+1}}else{n=Array(f);for(l=0;l<f;++l)n[l]=String.fromCharCode(u()[d+4+l]);n=n.join("")}T(d);return n},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||Y("Cannot pass non-string to std::string");var l=(c&&g?function(){return ya(f)}:function(){return f.length})(),h=S(4+l+1);z()[h>>2]=l;if(c&&g)xa(f,h+4,l+1);else if(g)for(g=0;g<l;++g){var n=f.charCodeAt(g);255<n&&(T(h),Y("String has UTF-16 code units that do not fit in 8 bits"));u()[h+4+g]=n}else for(g=0;g<l;++g)u()[h+4+g]=f[g];null!==d&&d.push(T,h);return h},
argPackAdvance:8,readValueFromPointer:jb,Ha:function(d){T(d)}})},p:function(a,b,c){c=X(c);if(2===b){var d=za;var f=Aa;var g=Ba;var l=function(){return ea()};var h=1}else 4===b&&(d=Ca,f=Da,g=Ea,l=function(){return z()},h=2);W(a,{name:c,fromWireType:function(n){for(var r=z()[n>>2],q=l(),v,x=n+4,E=0;E<=r;++E){var t=n+4+E*b;if(E==r||0==q[t>>h])x=d(x,t-x),void 0===v?v=x:(v+=String.fromCharCode(0),v+=x),x=t+b}T(n);return v},toWireType:function(n,r){"string"!==typeof r&&Y("Cannot pass non-string to C++ string type "+
c);var q=g(r),v=S(4+q+b);z()[v>>2]=q>>h;f(r,v+4,q+b);null!==n&&n.push(T,v);return v},argPackAdvance:8,readValueFromPointer:jb,Ha:function(n){T(n)}})},z:function(a,b,c,d,f,g){hb[a]={name:X(b),Bb:Db(c,d),Cb:Db(f,g),ab:[]}},i:function(a,b,c,d,f,g,l,h,n,r){hb[a].ab.push({nb:X(b),sb:c,qb:Db(d,f),rb:g,Lb:l,Kb:Db(h,n),Mb:r})},O:function(a,b){b=X(b);W(a,{Xb:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},I:function(a,b){if(a==b)postMessage({cmd:"processQueuedMainThreadWork"});
c);var q=g(r),v=S(4+q+b);z()[v>>2]=q>>h;f(r,v+4,q+b);null!==n&&n.push(T,v);return v},argPackAdvance:8,readValueFromPointer:jb,Ha:function(n){T(n)}})},z:function(a,b,c,d,f,g){hb[a]={name:X(b),Bb:Db(c,d),Cb:Db(f,g),ab:[]}},j:function(a,b,c,d,f,g,l,h,n,r){hb[a].ab.push({nb:X(b),sb:c,qb:Db(d,f),rb:g,Lb:l,Kb:Db(h,n),Mb:r})},O:function(a,b){b=X(b);W(a,{Xb:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},I:function(a,b){if(a==b)postMessage({cmd:"processQueuedMainThreadWork"});
else if(D)postMessage({targetThread:a,cmd:"processThreadQueue"});else{a=(a=R.Ga[a])&&a.worker;if(!a)return;a.postMessage({cmd:"processThreadQueue"})}return 1},r:ub,L:function(a){if(0===a)return vb(Kb());var b=Jb[a];a=void 0===b?X(a):b;return vb(Kb()[a])},S:function(a){4<a&&(Z[a].Wa+=1)},A:function(a,b,c,d){a||Y("Cannot use deleted val. handle = "+a);a=Z[a].value;var f=Mb[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")))(Lb,A,vb);Mb[b]=f}return f(a,c,d)},s:function(){J()},l:function(a,b,c){Tb.length=0;var d;for(c>>=2;d=u()[b++];)(d=105>d)&&c&1&&c++,
Tb.push(d?la()[c++>>1]:y()[c]),++c;return Qa[a].apply(null,Tb)},J:function(){},q:function(){},e:Nb,f:Wa,b:db,n:function(){return Ua|0},m:function(){return Ta|0},D:function(a,b,c){u().copyWithin(a,b,b+c)},T:function(){return navigator.hardwareConcurrency},F:function(a,b,c){Sb.length=b;c>>=3;for(var d=0;d<b;d++)Sb[d]=la()[c+d];return(0>a?Qa[-a-1]:nc[a]).apply(null,Sb)},k:function(a){a>>>=0;var b=u().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);
@@ -85,7 +85,7 @@ A._emscripten_sync_run_in_main_thread=function(){return(A._emscripten_sync_run_i
A._emscripten_sync_run_in_main_thread_2=function(){return(A._emscripten_sync_run_in_main_thread_2=A.asm.pa).apply(null,arguments)};A._emscripten_sync_run_in_main_thread_xprintf_varargs=function(){return(A._emscripten_sync_run_in_main_thread_xprintf_varargs=A.asm.qa).apply(null,arguments)};A._emscripten_sync_run_in_main_thread_3=function(){return(A._emscripten_sync_run_in_main_thread_3=A.asm.ra).apply(null,arguments)};
var pc=A._emscripten_sync_run_in_main_thread_4=function(){return(pc=A._emscripten_sync_run_in_main_thread_4=A.asm.sa).apply(null,arguments)};A._emscripten_sync_run_in_main_thread_5=function(){return(A._emscripten_sync_run_in_main_thread_5=A.asm.ta).apply(null,arguments)};A._emscripten_sync_run_in_main_thread_6=function(){return(A._emscripten_sync_run_in_main_thread_6=A.asm.ua).apply(null,arguments)};
A._emscripten_sync_run_in_main_thread_7=function(){return(A._emscripten_sync_run_in_main_thread_7=A.asm.va).apply(null,arguments)};var Rb=A._emscripten_run_in_main_runtime_thread_js=function(){return(Rb=A._emscripten_run_in_main_runtime_thread_js=A.asm.wa).apply(null,arguments)},Xb=A.__emscripten_call_on_thread=function(){return(Xb=A.__emscripten_call_on_thread=A.asm.xa).apply(null,arguments)};A._emscripten_tls_init=function(){return(A._emscripten_tls_init=A.asm.ya).apply(null,arguments)};
A.dynCall_jiii=function(){return(A.dynCall_jiii=A.asm.za).apply(null,arguments)};A.dynCall_jiiiiii=function(){return(A.dynCall_jiiiiii=A.asm.Aa).apply(null,arguments)};A.dynCall_jiji=function(){return(A.dynCall_jiji=A.asm.Ba).apply(null,arguments)};var Za=A._main_thread_futex=139852;A.PThread=R;A.PThread=R;A._pthread_self=jc;A.wasmMemory=k;A.ExitStatus=sc;var tc;function sc(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}O=function uc(){tc||vc();tc||(O=uc)};
A.dynCall_jiii=function(){return(A.dynCall_jiii=A.asm.za).apply(null,arguments)};A.dynCall_jiiiiii=function(){return(A.dynCall_jiiiiii=A.asm.Aa).apply(null,arguments)};A.dynCall_jiji=function(){return(A.dynCall_jiji=A.asm.Ba).apply(null,arguments)};var Za=A._main_thread_futex=139836;A.PThread=R;A.PThread=R;A._pthread_self=jc;A.wasmMemory=k;A.ExitStatus=sc;var tc;function sc(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}O=function uc(){tc||vc();tc||(O=uc)};
function vc(){function a(){if(!tc&&(tc=!0,A.calledRun=!0,!ua)){Ra(Ha);D||Ra(Ia);na(A);if(A.onRuntimeInitialized)A.onRuntimeInitialized();if(!D){if(A.postRun)for("function"==typeof A.postRun&&(A.postRun=[A.postRun]);A.postRun.length;){var b=A.postRun.shift();Ja.unshift(b)}Ra(Ja)}}}if(!(0<N)){if(!D){if(A.preRun)for("function"==typeof A.preRun&&(A.preRun=[A.preRun]);A.preRun.length;)Ka();Ra(Ga)}0<N||(A.setStatus?(A.setStatus("Running..."),setTimeout(function(){setTimeout(function(){A.setStatus("")},
1);a()},1)):a())}}A.run=vc;if(A.preInit)for("function"==typeof A.preInit&&(A.preInit=[A.preInit]);0<A.preInit.length;)A.preInit.pop()();D||(noExitRuntime=!0);D?R.yb():vc();

BIN
codecs/wp2/enc/wp2_enc_mt_simd.wasm Normal file → Executable file

Binary file not shown.

View File

@@ -36,12 +36,12 @@ toWireType:function(l,k){return k?d:e},argPackAdvance:8,readValueFromPointer:fun
toWireType:function(d,e){if("number"!==typeof e&&"boolean"!==typeof e)throw new TypeError('Cannot convert "'+Wa(e)+'" to '+this.name);return e},argPackAdvance:8,readValueFromPointer:Xa(b,c),K:null})},m:function(a,b,c,d,e,g){var l=ab(b,c);a=V(a);e=Y(d,e);$a(a,function(){fb("Cannot call "+a+" due to unbound types",l)},b-1);Pa([],l,function(k){var h=[k[0],null].concat(k.slice(1)),m=k=a,n=e,p=h.length;2>p&&W("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var x=null!==
h[1]&&!1,y=!1,q=1;q<h.length;++q)if(null!==h[q]&&void 0===h[q].K){y=!0;break}var fa="void"!==h[0].name,z="",F="";for(q=0;q<p-2;++q)z+=(0!==q?", ":"")+"arg"+q,F+=(0!==q?", ":"")+"arg"+q+"Wired";m="return function "+La(m)+"("+z+") {\nif (arguments.length !== "+(p-2)+") {\nthrowBindingError('function "+m+" called with ' + arguments.length + ' arguments, expected "+(p-2)+" args!');\n}\n";y&&(m+="var destructors = [];\n");var T=y?"destructors":"null";z="throwBindingError invoker fn runDestructors retType classParam".split(" ");
n=[W,n,g,Ia,h[0],h[1]];x&&(m+="var thisWired = classParam.toWireType("+T+", this);\n");for(q=0;q<p-2;++q)m+="var arg"+q+"Wired = argType"+q+".toWireType("+T+", arg"+q+"); // "+h[q+2].name+"\n",z.push("argType"+q),n.push(h[q+2]);x&&(F="thisWired"+(0<F.length?", ":"")+F);m+=(fa?"var rv = ":"")+"invoker(fn"+(0<F.length?", ":"")+F+");\n";if(y)m+="runDestructors(destructors);\n";else for(q=x?1:2;q<h.length;++q)p=1===q?"thisWired":"arg"+(q-2)+"Wired",null!==h[q].K&&(m+=p+"_dtor("+p+"); // "+h[q].name+"\n",
z.push(p+"_dtor"),n.push(h[q].K));fa&&(m+="var ret = retType.fromWireType(rv);\nreturn ret;\n");z.push(m+"}\n");h=Ya(z).apply(null,n);q=b-1;if(!f.hasOwnProperty(k))throw new Oa("Replacing nonexistant public symbol");void 0!==f[k].J&&void 0!==q?f[k].J[q]=h:(f[k]=h,f[k].R=q);return[]})},d:function(a,b,c,d,e){function g(m){return m}b=V(b);-1===e&&(e=4294967295);var l=Qa(c);if(0===d){var k=32-8*c;g=function(m){return m<<k>>>k}}var h=-1!=b.indexOf("unsigned");U(a,{name:b,fromWireType:g,toWireType:function(m,
z.push(p+"_dtor"),n.push(h[q].K));fa&&(m+="var ret = retType.fromWireType(rv);\nreturn ret;\n");z.push(m+"}\n");h=Ya(z).apply(null,n);q=b-1;if(!f.hasOwnProperty(k))throw new Oa("Replacing nonexistant public symbol");void 0!==f[k].J&&void 0!==q?f[k].J[q]=h:(f[k]=h,f[k].R=q);return[]})},c:function(a,b,c,d,e){function g(m){return m}b=V(b);-1===e&&(e=4294967295);var l=Qa(c);if(0===d){var k=32-8*c;g=function(m){return m<<k>>>k}}var h=-1!=b.indexOf("unsigned");U(a,{name:b,fromWireType:g,toWireType:function(m,
n){if("number"!==typeof n&&"boolean"!==typeof n)throw new TypeError('Cannot convert "'+Wa(n)+'" to '+this.name);if(n<d||n>e)throw new TypeError('Passing a number "'+Wa(n)+'" from JS side to C/C++ side to an argument of type "'+b+'", which is outside the valid range ['+d+", "+e+"]!");return h?n>>>0:n|0},argPackAdvance:8,readValueFromPointer:gb(b,l,0!==d),K:null})},b:function(a,b,c){function d(g){g>>=2;var l=J;return new e(H,l[g+1],l[g])}var e=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,
Uint32Array,Float32Array,Float64Array][b];c=V(c);U(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{X:!0})},k:function(a,b){b=V(b);var c="std::string"===b;U(a,{name:b,fromWireType:function(d){var e=J[d>>2];if(c)for(var g=d+4,l=0;l<=e;++l){var k=d+4+l;if(l==e||0==C[k]){g=B(g,k-g);if(void 0===h)var h=g;else h+=String.fromCharCode(0),h+=g;g=k+1}}else{h=Array(e);for(l=0;l<e;++l)h[l]=String.fromCharCode(C[d+4+l]);h=h.join("")}Z(d);return h},toWireType:function(d,e){e instanceof ArrayBuffer&&
(e=new Uint8Array(e));var g="string"===typeof e;g||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int8Array||W("Cannot pass non-string to std::string");var l=(c&&g?function(){for(var m=0,n=0;n<e.length;++n){var p=e.charCodeAt(n);55296<=p&&57343>=p&&(p=65536+((p&1023)<<10)|e.charCodeAt(++n)&1023);127>=p?++m:m=2047>=p?m+2:65535>=p?m+3:m+4}return m}:function(){return e.length})(),k=pb(4+l+1);J[k>>2]=l;if(c&&g)ma(e,k+4,l+1);else if(g)for(g=0;g<l;++g){var h=e.charCodeAt(g);255<h&&
(Z(k),W("String has UTF-16 code units that do not fit in 8 bits"));C[k+4+g]=h}else for(g=0;g<l;++g)C[k+4+g]=e[g];null!==d&&d.push(Z,k);return k},argPackAdvance:8,readValueFromPointer:Ja,K:function(d){Z(d)}})},f:function(a,b,c){c=V(c);if(2===b){var d=oa;var e=pa;var g=qa;var l=function(){return D};var k=1}else 4===b&&(d=ra,e=sa,g=ta,l=function(){return J},k=2);U(a,{name:c,fromWireType:function(h){for(var m=J[h>>2],n=l(),p,x=h+4,y=0;y<=m;++y){var q=h+4+y*b;if(y==m||0==n[q>>k])x=d(x,q-x),void 0===p?
p=x:(p+=String.fromCharCode(0),p+=x),x=q+b}Z(h);return p},toWireType:function(h,m){"string"!==typeof m&&W("Cannot pass non-string to C++ string type "+c);var n=g(m),p=pb(4+n+b);J[p>>2]=n>>k;e(m,p+4,n+b);null!==h&&h.push(Z,p);return p},argPackAdvance:8,readValueFromPointer:Ja,K:function(h){Z(h)}})},o:function(a,b,c,d,e,g){Q[a]={name:V(b),Z:Y(c,d),$:Y(e,g),O:[]}},c:function(a,b,c,d,e,g,l,k,h,m){Q[a].O.push({S:V(b),W:c,U:Y(d,e),V:g,ha:l,ga:Y(k,h),ia:m})},x:function(a,b){b=V(b);U(a,{ja:!0,name:b,argPackAdvance:0,
p=x:(p+=String.fromCharCode(0),p+=x),x=q+b}Z(h);return p},toWireType:function(h,m){"string"!==typeof m&&W("Cannot pass non-string to C++ string type "+c);var n=g(m),p=pb(4+n+b);J[p>>2]=n>>k;e(m,p+4,n+b);null!==h&&h.push(Z,p);return p},argPackAdvance:8,readValueFromPointer:Ja,K:function(h){Z(h)}})},o:function(a,b,c,d,e,g){Q[a]={name:V(b),Z:Y(c,d),$:Y(e,g),O:[]}},d:function(a,b,c,d,e,g,l,k,h,m){Q[a].O.push({S:V(b),W:c,U:Y(d,e),V:g,ha:l,ga:Y(k,h),ia:m})},x:function(a,b){b=V(b);U(a,{ja:!0,name:b,argPackAdvance:0,
fromWireType:function(){},toWireType:function(){}})},i:Ua,A:function(a){if(0===a)return Va(ib());var b=hb[a];a=void 0===b?V(a):b;return Va(ib()[a])},l:function(a){4<a&&(X[a].N+=1)},p:function(a,b,c,d){a||W("Cannot use deleted val. handle = "+a);a=X[a].value;var e=kb[b];if(!e){e="";for(var g=0;g<b;++g)e+=(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";e=(new Function("requireRegisteredType","Module","__emval_register",l+("var obj = new constructor("+e+");\nreturn __emval_register(obj);\n}\n")))(jb,f,Va);kb[b]=e}return e(a,c,d)},g:function(){u()},t:function(a,b,c){C.copyWithin(a,b,b+c)},e:function(a){a>>>=0;var b=C.length;if(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{A.grow(Math.min(2147483648,d)-H.byteLength+65535>>>16);wa(A.buffer);var e=1;break a}catch(g){}e=void 0}if(e)return!0}return!1},u:function(){return 0},r:function(){},h:function(a,b,c,d){for(var e=0,g=0;g<c;g++){for(var l=G[b+8*g>>2],k=G[b+(8*g+4)>>2],h=0;h<k;h++){var m=C[l+h],n=lb[a];if(0===m||10===m){m=1===a?ja:v;var p;for(p=0;n[p]&&!(NaN<=p);)++p;p=la.decode(n.subarray?n.subarray(0,p):new Uint8Array(n.slice(0,p)));m(p);n.length=0}else n.push(m)}e+=k}G[d>>

Binary file not shown.

6
package-lock.json generated
View File

@@ -6036,9 +6036,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": {

View File

@@ -35,7 +35,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",
"rollup": "^2.33.1",

View File

@@ -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}
/>
);
}
}

View File

@@ -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 });
});
}
}

View File

@@ -1,210 +0,0 @@
import {
h,
cloneElement,
Component,
VNode,
createRef,
ComponentChildren,
ComponentProps,
Fragment,
render,
} from 'preact';
import * as style from './style.css';
import 'add-css:./style.css';
type Anchor = 'left' | 'right' | 'top' | 'bottom';
type Direction = 'left' | 'right' | 'up' | 'down';
const has = (haystack: string | string[] | undefined, needle: string) =>
Array.isArray(haystack) ? haystack.includes(needle) : haystack === needle;
interface Props extends Omit<ComponentProps<'aside'>, 'ref'> {
showing?: boolean;
direction?: Direction | Direction[];
anchor?: Anchor;
toggle?: VNode;
children?: ComponentChildren;
}
interface State {
showing: boolean;
hasShown: boolean;
}
export default class Flyout extends Component<Props, State> {
state = {
showing: this.props.showing === true,
hasShown: this.props.showing === true,
};
private wrap = createRef<HTMLElement>();
private menu = createRef<HTMLElement>();
private resizeObserver?: ResizeObserver;
private shown?: number;
private dismiss = (event: Event) => {
if (this.menu.current && this.menu.current.contains(event.target as Node))
return;
// prevent toggle buttons from immediately dismissing:
if (this.shown && Date.now() - this.shown < 10) return;
this.setShowing(false);
};
hide = () => {
this.setShowing(false);
};
show = () => {
this.setShowing(true);
};
toggle = () => {
this.setShowing(!this.state.showing);
};
private setShowing = (showing?: boolean) => {
this.shown = Date.now();
if (showing) this.setState({ showing: true, hasShown: true });
else this.setState({ showing: false });
};
private reposition = () => {
const menu = this.menu.current;
const wrap = this.wrap.current;
if (!menu || !wrap || !this.state.showing) return;
const bbox = wrap.getBoundingClientRect();
const { direction = 'down', anchor = 'right' } = this.props;
const { innerWidth, innerHeight } = window;
const anchorX = has(anchor, 'left') ? bbox.left : bbox.right;
menu.style.left = menu.style.right = menu.style.top = menu.style.bottom =
'';
if (has(direction, 'left')) {
menu.style.right = innerWidth - anchorX + 'px';
} else {
menu.style.left = anchorX + 'px';
}
if (has(direction, 'up')) {
const anchorY = has(anchor, 'bottom') ? bbox.bottom : bbox.top;
menu.style.bottom = innerHeight - anchorY + 'px';
} else {
const anchorY = has(anchor, 'top') ? bbox.top : bbox.bottom;
menu.style.top = anchorY + 'px';
}
};
componentWillReceiveProps({ showing }: Props) {
if (showing !== this.props.showing) {
this.setShowing(showing);
}
}
componentDidMount() {
addEventListener('click', this.dismiss, { passive: true });
addEventListener('resize', this.reposition, { passive: true });
if (typeof ResizeObserver === 'function' && this.wrap.current) {
this.resizeObserver = new ResizeObserver(this.reposition);
this.resizeObserver.observe(this.wrap.current);
}
if (this.props.showing) this.setShowing(true);
}
componentWillUnmount() {
removeEventListener('click', this.dismiss);
removeEventListener('resize', this.reposition);
if (this.resizeObserver) this.resizeObserver.disconnect();
}
componentDidUpdate(prevProps: Props, prevState: State) {
if (this.state.showing && !prevState.showing) {
const menu = this.menu.current;
if (menu) {
this.reposition();
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 directionText = Array.isArray(direction)
? direction.join(' ')
: direction;
const anchorText = Array.isArray(anchor) ? anchor.join(' ') : anchor;
return (
<span
class={style.wrap}
ref={this.wrap}
data-flyout-open={showing ? '' : undefined}
>
{toggle && cloneElement(toggle, toggleProps)}
{showing &&
createPortal(
<aside
{...props}
class={`${style.flyout} ${props.class || props.className || ''}`}
ref={this.menu}
data-anchor={anchorText}
data-direction={directionText}
>
{children}
</aside>,
document.body,
)}
</span>
);
}
}
// not worth pulling in compat
function createPortal(children: ComponentChildren, parent: Element) {
return <Portal parent={parent}>{children}</Portal>;
}
// this is probably overly careful, since it works directly rendering into parent
function createPersistentFragment(parent: Element) {
const frag = {
nodeType: 11,
childNodes: [],
appendChild: parent.appendChild.bind(parent),
insertBefore: parent.insertBefore.bind(parent),
removeChild: parent.removeChild.bind(parent),
};
return (frag as unknown) as Element;
}
class Portal extends Component<{
children: ComponentChildren;
parent: Element;
}> {
root = createPersistentFragment(this.props.parent);
componentWillUnmount() {
render(null, this.root);
}
render() {
render(<Fragment>{this.props.children}</Fragment>, this.root);
return null;
}
}

View File

@@ -1,41 +0,0 @@
.wrap {
position: relative;
display: flex;
align-items: center;
justify-items: center;
}
.flyout {
display: inline-block;
position: fixed;
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;
}
&[data-direction*='left'] {
align-items: flex-end;
}
&[data-direction*='up'] {
--flyout-offset-y: 20px;
flex-direction: column-reverse;
}
}
@keyframes menuOpen {
0% {
transform: translateY(var(--flyout-offset-y, 0));
opacity: 0;
}
}

View File

@@ -1,4 +1,4 @@
import { h, Component, createRef } from 'preact';
import { h, Component } from 'preact';
import * as style from './style.css';
import 'add-css:./style.css';
@@ -15,10 +15,9 @@ import {
import Expander from './Expander';
import Toggle from './Toggle';
import Select from './Select';
import Flyout from '../Flyout';
import { Options as QuantOptionsComponent } from 'features/processors/quantize/client';
import { Options as ResizeOptionsComponent } from 'features/processors/resize/client';
import { CLIIcon, MoreIcon, SwapIcon } from 'client/lazy-app/icons';
import { CLIIcon, SwapIcon } from 'client/lazy-app/icons';
interface Props {
index: 0 | 1;
@@ -65,8 +64,6 @@ export default class Options extends Component<Props, State> {
supportedEncoderMap: undefined,
};
menu = createRef<Flyout>();
constructor() {
super();
supportedEncoderMapP.then((supportedEncoderMap) =>
@@ -113,12 +110,10 @@ export default class Options extends Component<Props, State> {
private onCopyCliClick = () => {
this.props.onCopyCliClick(this.props.index);
if (this.menu.current) this.menu.current.hide();
};
private onCopyToOtherSideClick = () => {
this.props.onCopyToOtherSideClick(this.props.index);
if (this.menu.current) this.menu.current.hide();
};
render(
@@ -141,33 +136,23 @@ export default class Options extends Component<Props, State> {
{!encoderState ? null : (
<div>
<h3 class={style.optionsTitle}>
Edit
<Flyout
ref={this.menu}
class={style.menu}
direction={['up', 'left']}
anchor="right"
toggle={
<button class={style.titleButton}>
<MoreIcon />
</button>
}
>
<div class={style.titleAndButtons}>
Edit
<button
class={style.menuButton}
class={style.cliButton}
title="Copy npx command"
onClick={this.onCopyCliClick}
>
<CLIIcon />
Copy npx command
</button>
<button
class={style.menuButton}
class={style.copyOverButton}
title="Copy settings to other side"
onClick={this.onCopyToOtherSideClick}
>
<SwapIcon />
Copy settings to other side
</button>
</Flyout>
</div>
</h3>
<label class={style.sectionEnabler}>
Resize

View File

@@ -14,21 +14,13 @@
background-color: var(--main-theme-color);
color: var(--header-text-color);
margin: 0;
height: 38px;
padding: 0 var(--horizontal-padding);
padding: 10px var(--horizontal-padding);
font-weight: bold;
font-size: 1.4rem;
border-bottom: 1px solid var(--off-black);
transition: all 300ms ease-in-out;
transition-property: background-color, color;
display: grid;
align-items: center;
grid-template-columns: 1fr;
grid-auto-columns: max-content;
grid-auto-flow: column;
gap: 0.8rem 0;
position: sticky;
top: 0;
z-index: 1;
@@ -90,63 +82,36 @@
border-radius: 4px;
}
.menu {
transform: translateY(-10px);
.title-and-buttons {
grid-template-columns: 1fr;
grid-auto-columns: max-content;
grid-auto-flow: column;
display: grid;
gap: 0.8rem;
}
.title-button {
position: relative;
left: 10px;
composes: unbutton from global;
border-radius: 50%;
background: rgba(255, 255, 255, 0);
&:hover,
&:active {
background: rgba(255, 255, 255, 0.3);
}
svg {
--size: 24px;
fill: var(--header-text-color);
--size: 20px;
display: block;
width: var(--size);
height: var(--size);
padding: 5px;
}
}
.menu-button {
display: flex;
align-items: center;
box-sizing: border-box;
margin: 8px 0;
background-color: rgba(29, 29, 29, 0.92);
border: 1px solid rgba(0, 0, 0, 0.67);
border-radius: 2rem;
line-height: 1.1;
white-space: nowrap;
height: 39px;
padding: 0 16px;
font-size: 1.2rem;
cursor: pointer;
color: #fff;
.cli-button {
composes: title-button;
&:hover {
background: rgba(50, 50, 50, 0.92);
}
&:focus {
box-shadow: 0 0 0 2px #fff;
outline: none;
z-index: 1;
}
& > svg {
position: relative;
width: 18px;
height: 18px;
margin-right: 12px;
color: var(--main-theme-color);
svg {
stroke: var(--header-text-color);
}
}
.copy-over-button {
composes: title-button;
svg {
fill: var(--header-text-color);
}
}

View File

@@ -1,4 +1,4 @@
import { h, createRef, Component, Fragment } from 'preact';
import { h, Component, Fragment } from 'preact';
import type PinchZoom from './custom-els/PinchZoom';
import type { ScaleToOpts } from './custom-els/PinchZoom';
import './custom-els/PinchZoom';
@@ -10,37 +10,30 @@ import {
ToggleBackgroundIcon,
AddIcon,
RemoveIcon,
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';
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;
onPreprocessorChange: (newState: PreprocessorState) => void;
}
interface State {
scale: number;
editingScale: boolean;
altBackground: boolean;
transform: boolean;
menuOpen: boolean;
smallControls: boolean;
}
const scaleToOpts: ScaleToOpts = {
@@ -55,18 +48,12 @@ export default class Output extends Component<Props, State> {
scale: 1,
editingScale: false,
altBackground: false,
transform: false,
menuOpen: false,
smallControls:
typeof matchMedia === 'function' &&
matchMedia('(max-width: 859px)').matches,
};
canvasLeft?: HTMLCanvasElement;
canvasRight?: HTMLCanvasElement;
pinchZoomLeft?: PinchZoom;
pinchZoomRight?: PinchZoom;
scaleInput?: HTMLInputElement;
flyout = createRef<Flyout>();
retargetedEvents = new WeakSet<Event>();
componentDidMount() {
@@ -88,12 +75,6 @@ export default class Output extends Component<Props, State> {
if (this.canvasRight && rightDraw) {
drawDataToCanvas(this.canvasRight, rightDraw);
}
if (typeof matchMedia === 'function') {
matchMedia('(max-width: 859px)').addEventListener('change', (e) =>
this.setState({ smallControls: e.matches }),
);
}
}
componentDidUpdate(prevProps: Props, prevState: State) {
@@ -163,6 +144,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);
@@ -173,30 +160,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),
);
this.pinchZoomLeft.scaleTo(Number(scale.toFixed(2)), scaleToOpts);
this.recenter();
// this.hideMenu();
};
private onRotateClick = () => {
const { preprocessorState: inputProcessorState } = this.props;
if (!inputProcessorState) return;
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,
});
const newState = cleanSet(
inputProcessorState,
'rotate.rotate',
(inputProcessorState.rotate.rotate + 90) % 360,
);
this.props.onPreprocessorChange(newState);
};
private onScaleValueFocus = () => {
@@ -279,16 +253,8 @@ export default class Output extends Component<Props, State> {
};
render(
{
source,
mobileView,
hidden,
leftImgContain,
rightImgContain,
onShowPreprocessorTransforms,
onToggleBackground,
}: Props,
{ scale, editingScale, smallControls }: State,
{ mobileView, leftImgContain, rightImgContain, source }: Props,
{ scale, editingScale, altBackground }: State,
) {
const leftDraw = this.leftDrawable();
const rightDraw = this.rightDrawable();
@@ -297,7 +263,9 @@ export default class Output extends Component<Props, State> {
return (
<Fragment>
<div class={style.output} hidden={hidden}>
<div
class={`${style.output} ${altBackground ? style.altBackground : ''}`}
>
<two-up
legacy-clip-compat
class={style.twoUp}
@@ -323,7 +291,7 @@ export default class Output extends Component<Props, State> {
style={{
width: originalImage ? originalImage.width : '',
height: originalImage ? originalImage.height : '',
objectFit: leftImgContain ? 'contain' : undefined,
objectFit: leftImgContain ? 'contain' : '',
}}
/>
</pinch-zoom>
@@ -339,16 +307,15 @@ export default class Output extends Component<Props, State> {
style={{
width: originalImage ? originalImage.width : '',
height: originalImage ? originalImage.height : '',
objectFit: rightImgContain ? 'contain' : undefined,
objectFit: rightImgContain ? 'contain' : '',
}}
/>
</pinch-zoom>
</two-up>
</div>
<div class={style.controls} hidden={hidden}>
<div class={style.controls}>
<div class={style.buttonGroup}>
<button class={style.button} onClick={this.zoomOut}>
<button class={style.firstButton} onClick={this.zoomOut}>
<RemoveIcon />
</button>
{editingScale ? (
@@ -375,34 +342,18 @@ export default class Output extends Component<Props, State> {
<button class={style.lastButton} onClick={this.zoomIn}>
<AddIcon />
</button>
<Flyout
class={style.menu}
showing={hidden ? false : undefined}
anchor="right"
direction={smallControls ? ['down', 'left'] : 'up'}
toggle={
<button class={`${style.button} ${style.moreButton}`}>
<MoreIcon />
</button>
}
>
<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.recenter}>
Re-center
</button>
<button class={style.button} onClick={onToggleBackground}>
<ToggleBackgroundIcon /> Change canvas color
</button>
</Flyout>
</div>
<div class={style.buttonGroup}>
<button class={style.firstButton} onClick={this.onRotateClick}>
<RotateIcon />
</button>
<button class={style.lastButton} onClick={this.toggleBackground}>
{altBackground ? (
<ToggleBackgroundActiveIcon />
) : (
<ToggleBackgroundIcon />
)}
</button>
</div>
</div>
</Fragment>

View File

@@ -1,8 +1,20 @@
.output {
display: contents;
&[hidden] {
display: none;
&::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;
}
}
@@ -30,21 +42,16 @@
.controls {
display: flex;
justify-content: center;
overflow: hidden;
flex-wrap: wrap;
contain: content;
grid-area: header;
align-self: center;
padding: 9px 66px;
position: relative;
/* Had to disable containment because of the overflow menu. */
/*
contain: content;
overflow: hidden;
*/
transition: transform 500ms ease;
/* Allow clicks to fall through to the pinch zoom area */
pointer-events: none;
& > * {
pointer-events: auto;
}
@@ -55,34 +62,13 @@
grid-area: viewportOpts;
align-self: end;
}
&[hidden] {
visibility: visible;
transform: translateY(-200%);
@media (min-width: 860px) {
transform: translateY(200%);
}
}
}
.button-group {
display: flex;
position: relative;
z-index: 100;
& > :not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
margin-left: 0;
}
& > :not(:nth-last-child(2)) {
margin-right: 0;
border-right-width: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
margin: 0 3px;
}
.button,
@@ -90,10 +76,9 @@
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;
border-width: 1px 0 1px 1px;
line-height: 1.1;
white-space: nowrap;
height: 39px;
@@ -176,64 +161,3 @@ input.zoom {
pointer-events: auto;
}
}
/** Three-dot menu */
.moreButton {
padding: 0 4px;
& > svg {
transform-origin: center;
transition: transform 200ms ease;
}
}
.controls [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;
}
}

View File

@@ -1,330 +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 }[];
aspect: number | undefined;
}
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;
}
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();
let aspect;
const edges = edgeAttr.split(/ *, */) as Edge[];
if (this.props.lockAspect) {
if (edges.length === 1) return;
const { size } = this.props;
const oldCrop = this.state.crop;
aspect =
(size.width - oldCrop.left - oldCrop.right) /
(size.height - oldCrop.top - oldCrop.bottom);
}
this.pointers.set(event.pointerId, {
x: event.x,
y: event.y,
edges: edges.map((edge) => ({ edge, value: this.state.crop[edge] })),
aspect,
});
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 scale = this.props.scale || 1;
let dx = (event.x - down.x) / scale;
let dy = (event.y - down.y) / scale;
if (down.aspect && down.edges.length === 2) {
const dir = (dx + dy) / 2;
dx = dir * down.aspect;
dy = dir / down.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) {
if (down.aspect) crop.left = Math.max(0, crop.left);
else
crop.left = Math.min(
crop.left,
size.width - oldCrop.right - MIN_SIZE,
);
}
if (crop.right) {
if (down.aspect) crop.right = Math.max(0, crop.right);
crop.right = Math.min(
crop.right,
size.width - oldCrop.left - MIN_SIZE,
);
}
if (
down.aspect &&
(crop.left ?? oldCrop.left) + (crop.right ?? oldCrop.right) >
size.width
)
return;
}
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) {
if (down.aspect) crop.top = Math.max(0, crop.top);
crop.top = Math.min(
crop.top,
size.height - oldCrop.bottom - MIN_SIZE,
);
}
if (crop.bottom) {
if (down.aspect) crop.bottom = Math.max(0, crop.bottom);
crop.bottom = Math.min(
crop.bottom,
size.height - oldCrop.top - MIN_SIZE,
);
}
if (
down.aspect &&
(crop.top ?? oldCrop.top) + (crop.bottom ?? oldCrop.bottom) >
size.height
)
return;
}
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 s = (x: number) => x / (scale || 1);
const clip = `polygon(0 0, 0 100%, 100% 100%, 100% 0, 0 0, ${s(x)}px ${s(
y,
)}px, ${s(x + width)}px ${s(y)}px, ${s(x + width)}px ${s(
y + height,
)}px, ${s(x)}px ${s(y + height)}px, ${s(x)}px ${s(y)}px)`;
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}
>
<rect
class={style.background}
width={size.width}
height={size.height}
clip-path={clip}
/>
<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%"
/>
<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" />
<circle class={style.corner} data-edge="right,top" cx="100%" />
<circle
class={style.corner}
data-edge="right,bottom"
cx="100%"
cy="100%"
/>
<circle class={style.corner} data-edge="left,bottom" cy="100%" />
</Freezer>
</svg>
</svg>
);
}
}
interface FreezerProps {
children: ComponentChildren;
}
class Freezer extends Component<FreezerProps> {
shouldComponentUpdate() {
return false;
}
render({ children }: FreezerProps) {
return children;
}
}

View File

@@ -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.5px / var(--scale, 1));
stroke-dasharray: calc(5px / var(--scale, 1)), calc(5px / 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(4px / var(--scale, 1));
stroke-width: calc(4px / 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;
}
}

View File

@@ -1,587 +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);
if (onSave) onSave({ preprocessorState: newState });
};
private cancel = () => {
const { onCancel, onSave } = this.props;
if (onCancel) onCancel();
else if (onSave)
onSave({ preprocessorState: this.props.preprocessorState });
};
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;
const cropPreset = value ? (value as keyof typeof cropPresets) : undefined;
const crop = { ...this.state.crop };
if (cropPreset) {
const preset = cropPresets[cropPreset];
const { width, height } = this.props.source.decoded;
const w = width - crop.left - crop.right;
const h = w / preset.ratio;
crop.bottom = height - crop.top - h;
if (crop.bottom < 0) {
crop.top += crop.bottom;
crop.bottom = 0;
}
}
this.setState({
crop,
cropPreset,
lockAspect: !!cropPreset,
});
};
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 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 });
};
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;
}
/** @TODO this could at least use clip-path. It's too expensive this way. */
class Backdrop extends Component<BackdropProps> {
shouldComponentUpdate({ width, height }: BackdropProps) {
return width !== this.props.width || height !== this.props.height;
}
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>
);
}
}

View File

@@ -1,356 +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.7);
& > 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.7);
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);
/* Hide on mobile (for now) */
@media (max-width: 599px) {
display: none;
}
}
@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;
}
}

View File

@@ -11,7 +11,6 @@ import {
canDecodeImageType,
abortable,
assertSignal,
shallowEqual,
} from '../util';
import {
PreprocessorState,
@@ -32,7 +31,7 @@ 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 Transform from './Transform';
import { Arrow, ExpandIcon } from '../icons';
import { generateCliInvocation } from '../util/cli';
export type OutputType = EncoderType | 'identity';
@@ -70,11 +69,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;
}
@@ -130,18 +126,13 @@ async function preprocessImage(
): Promise<ImageData> {
assertSignal(signal);
let processedData = data;
const { rotate, flip, crop } = preprocessorState;
if (flip && (flip.horizontal || flip.vertical)) {
processedData = await workerBridge.flip(signal, processedData, flip);
}
if (rotate.rotate !== 0) {
processedData = await workerBridge.rotate(signal, processedData, rotate);
}
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;
@@ -283,9 +274,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: [
{
@@ -307,7 +295,6 @@ export default class Compress extends Component<Props, State> {
},
],
mobileView: this.widthQuery.matches,
altBackground: false,
};
private readonly encodeCache = new ResultCache();
@@ -333,12 +320,6 @@ 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 => {
this.setState({
sides: cleanSet(
@@ -380,19 +361,6 @@ export default class Compress extends Component<Props, State> {
});
};
private showPreprocessorTransforms = () => {
this.setState({ transform: true });
};
private onTransformUpdated = ({
preprocessorState,
}: { preprocessorState?: PreprocessorState } = {}) => {
if (preprocessorState) {
this.onPreprocessorChange(preprocessorState);
}
this.setState({ transform: false });
};
componentWillReceiveProps(nextProps: Props): void {
if (nextProps.file !== this.props.file) {
this.sourceFile = nextProps.file;
@@ -449,38 +417,25 @@ export default class Compress extends Component<Props, State> {
const newRotate = preprocessorState.rotate.rotate;
const orientationChanged = oldRotate % 180 !== newRotate % 180;
const { crop } = preprocessorState;
const cropChanged = !shallowEqual(crop, this.state.preprocessorState.crop);
this.setState((state) => ({
loading: true,
preprocessorState,
// Flip resize values if orientation has changed
sides:
!orientationChanged && !cropChanged
? state.sides
: (state.sides.map((side) => {
const currentResizeSettings =
side.latestSettings.processorState.resize;
let resizeSettings: Partial<ProcessorState['resize']>;
if (cropChanged) {
const img = state.source?.decoded;
resizeSettings = {
width: img ? img.width - crop.left - crop.right : undefined,
height: img ? img.height - crop.top - crop.bottom : undefined,
};
} else {
resizeSettings = {
width: currentResizeSettings.height,
height: currentResizeSettings.width,
};
}
return cleanMerge(
side,
'latestSettings.processorState.resize',
resizeSettings,
);
}) as [Side, Side]),
sides: !orientationChanged
? state.sides
: (state.sides.map((side) => {
const currentResizeSettings =
side.latestSettings.processorState.resize;
const resizeSettings: Partial<ProcessorState['resize']> = {
width: currentResizeSettings.height,
height: currentResizeSettings.width,
};
return cleanMerge(
side,
'latestSettings.processorState.resize',
resizeSettings,
);
}) as [Side, Side]),
}));
};
@@ -858,22 +813,12 @@ export default class Compress extends Component<Props, State> {
}
render(
{ onBack, showSnack }: Props,
{
loading,
sides,
source,
mobileView,
altBackground,
transform,
preprocessorState,
}: State,
{ onBack }: Props,
{ 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}
@@ -918,13 +863,8 @@ 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}
@@ -933,8 +873,6 @@ export default class Compress extends Component<Props, State> {
rightImgContain={rightImgContain}
preprocessorState={preprocessorState}
onPreprocessorChange={this.onPreprocessorChange}
onShowPreprocessorTransforms={this.showPreprocessorTransforms}
onToggleBackground={this.toggleBackground}
/>
<button class={style.back} onClick={onBack}>
<svg viewBox="0 0 61 53.3">
@@ -970,16 +908,6 @@ export default class Compress extends Component<Props, State> {
</div>,
]
)}
{transform && (
<Transform
mobileView={mobileView}
source={source!}
preprocessorState={preprocessorState!}
onSave={this.onTransformUpdated}
onCancel={this.onTransformUpdated}
/>
)}
</div>
);
}

View File

@@ -17,47 +17,6 @@
'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%);
}
@media (max-width: 599px) {
& > .options {
display: none;
}
}
& > .back {
display: none;
}
& > :first-child {
display: none;
}
}
}
.options {
@@ -74,7 +33,6 @@
grid-template-rows: 1fr max-content;
align-content: end;
align-self: end;
transition: transform 500ms ease;
@media (min-width: 600px) {
width: 300px;

View File

@@ -1,6 +1,6 @@
import { h } from 'preact';
const Icon = (props: preact.JSX.HTMLAttributes | preact.JSX.SVGAttributes) => (
const Icon = (props: preact.JSX.HTMLAttributes) => (
// @ts-ignore - TS bug https://github.com/microsoft/TypeScript/issues/16019
<svg
width="24"
@@ -11,56 +11,6 @@ const Icon = (props: preact.JSX.HTMLAttributes | preact.JSX.SVGAttributes) => (
/>
);
export const CLIIcon = (props: preact.JSX.HTMLAttributes) => (
<Icon {...props}>
<path d="M1 2.7H23v18.5H1zm5.5 13l3.7-3.7-3.7-3.7m5.5 7.4h5.6" />
</Icon>
);
export const SwapIcon = (props: preact.JSX.HTMLAttributes) => (
<Icon {...props}>
<path d="M8.5 8.6v6.8L5.1 12l3.4-3.4M10 5l-7 7 7 7V5zm4 0v14l7-7-7-7z" />
</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 ToggleBackgroundIcon = (props: preact.JSX.HTMLAttributes) => (
<Icon {...props}>
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3c0 1.1.9 2 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z" />
@@ -81,14 +31,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" />
<circle cx="12" cy="12" r="2" />
<circle cx="12" cy="18" r="2" />
</Icon>
);
export const AddIcon = (props: preact.JSX.HTMLAttributes) => (
<Icon {...props}>
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
@@ -130,3 +72,20 @@ export const DownloadIcon = () => (
<path d="M6.6 2.7h-4v13.2h2.7A2.7 2.7 0 018 18.6a2.7 2.7 0 002.6 2.6h2.7a2.7 2.7 0 002.6-2.6 2.7 2.7 0 012.7-2.7h2.6V2.7h-4a1.3 1.3 0 110-2.7h4A2.7 2.7 0 0124 2.7v18.5a2.7 2.7 0 01-2.7 2.7H2.7A2.7 2.7 0 010 21.2V2.7A2.7 2.7 0 012.7 0h4a1.3 1.3 0 010 2.7zm4 7.4V1.3a1.3 1.3 0 112.7 0v8.8L15 8.4a1.3 1.3 0 011.9 1.8l-4 4a1.3 1.3 0 01-1.9 0l-4-4A1.3 1.3 0 019 8.4z" />
</svg>
);
export const CLIIcon = () => (
<svg viewBox="0 0 81.3 68.8">
<path
fill="none"
stroke-miterlimit="15.6"
stroke-width="6.3"
d="M3.1 3.1h75v62.5h-75zm18.8 43.8l12.5-12.5-12.5-12.5m18.7 25h18.8"
/>
</svg>
);
export const SwapIcon = () => (
<svg viewBox="0 0 18 14">
<path d="M5.5 3.6v6.8L2.1 7l3.4-3.4M7 0L0 7l7 7V0zm4 0v14l7-7-7-7z" />
</svg>
);

View File

@@ -10,6 +10,7 @@ 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';
import { TileShape } from 'codecs/wp2/enc/wp2_enc';
export const encode = (
signal: AbortSignal,
@@ -35,9 +36,9 @@ interface State {
slightLoss: number;
colorSpace: number;
errorDiffusion: number;
useRandomMatrix: boolean;
showAdvanced: boolean;
separateAlpha: boolean;
tileShape: number;
}
export class Options extends Component<Props, State> {
@@ -60,8 +61,8 @@ export class Options extends Component<Props, State> {
uvMode: options.uv_mode,
colorSpace: options.csp_type,
errorDiffusion: options.error_diffusion,
useRandomMatrix: options.use_random_matrix,
separateAlpha: options.quality !== options.alpha_quality,
tileShape: options.tile_shape,
};
// If quality is > 95, it's lossless with slight loss
@@ -120,7 +121,7 @@ export class Options extends Component<Props, State> {
uv_mode: optionState.uvMode,
csp_type: optionState.colorSpace,
error_diffusion: optionState.errorDiffusion,
use_random_matrix: optionState.useRandomMatrix,
tile_shape: optionState.tileShape,
};
// Updating options, so we don't recalculate in getDerivedStateFromProps.
@@ -148,9 +149,9 @@ export class Options extends Component<Props, State> {
slightLoss,
colorSpace,
errorDiffusion,
useRandomMatrix,
separateAlpha,
showAdvanced,
tileShape,
}: State,
) {
return (
@@ -213,17 +214,23 @@ export class Options extends Component<Props, State> {
</div>
)}
</Expander>
<label class={style.optionReveal}>
<Revealer
checked={showAdvanced}
onChange={linkState(this, 'showAdvanced')}
/>
Advanced settings
</label>
</div>
)}
</Expander>
<label class={style.optionReveal}>
<Revealer
checked={showAdvanced}
onChange={linkState(this, 'showAdvanced')}
/>
Advanced settings
</label>
<Expander>
{showAdvanced && (
<div>
<Expander>
{showAdvanced && (
{!lossless && (
<div>
<div class={style.optionOneCell}>
{/*<div class={style.optionOneCell}>
<Range
min="1"
max="10"
@@ -233,7 +240,7 @@ export class Options extends Component<Props, State> {
>
Passes:
</Range>
</div>
</div>*/}
<div class={style.optionOneCell}>
<Range
min="0"
@@ -245,17 +252,6 @@ export class Options extends Component<Props, State> {
Spatial noise shaping:
</Range>
</div>
<div class={style.optionOneCell}>
<Range
min="0"
max="100"
step="1"
value={errorDiffusion}
onInput={this._inputChange('errorDiffusion', 'number')}
>
Error diffusion:
</Range>
</div>
<label class={style.optionTextFirst}>
Subsample chroma:
<Select
@@ -263,7 +259,7 @@ export class Options extends Component<Props, State> {
onInput={this._inputChange('uvMode', 'number')}
>
<option value={UVMode.UVModeAuto}>Auto</option>
<option value={UVMode.UVModeAdapt}>Vary</option>
<option value={UVMode.UVModeAdapt}>Mixed</option>
<option value={UVMode.UVMode420}>Half</option>
<option value={UVMode.UVMode444}>Off</option>
</Select>
@@ -276,22 +272,37 @@ export class Options extends Component<Props, State> {
>
<option value={Csp.kYCoCg}>YCoCg</option>
<option value={Csp.kYCbCr}>YCbCr</option>
<option value={Csp.kCustom}>Adaptive</option>
<option value={Csp.kYIQ}>YIQ</option>
</Select>
</label>
<label class={style.optionToggle}>
Random matrix
<Checkbox
checked={useRandomMatrix}
onChange={this._inputChange(
'useRandomMatrix',
'boolean',
)}
/>
</label>
<div class={style.optionOneCell}>
<Range
min="0"
max="100"
step="1"
value={errorDiffusion}
onInput={this._inputChange('errorDiffusion', 'number')}
>
Error diffusion:
</Range>
</div>
</div>
)}
</Expander>
<label class={style.optionTextFirst}>
Tile shape:
<Select
value={tileShape}
onInput={this._inputChange('tileShape', 'number')}
>
<option value={TileShape.Auto}>Auto</option>
<option value={TileShape.Square128}>128x128</option>
<option value={TileShape.Square256}>256x256</option>
<option value={TileShape.Square512}>512x512</option>
<option value={TileShape.Wide}>Maximum</option>
</Select>
</label>
</div>
)}
</Expander>

View File

@@ -11,7 +11,7 @@
* limitations under the License.
*/
import type { EncodeOptions } from 'codecs/wp2/enc/wp2_enc';
import { UVMode, Csp } from 'codecs/wp2/enc/wp2_enc';
import { UVMode, Csp, TileShape } from 'codecs/wp2/enc/wp2_enc';
export { EncodeOptions, UVMode, Csp };
@@ -27,5 +27,5 @@ export const defaultOptions: EncodeOptions = {
uv_mode: UVMode.UVModeAuto,
csp_type: Csp.kYCoCg,
error_diffusion: 0,
use_random_matrix: false,
tile_shape: TileShape.Auto,
};

View File

@@ -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,
};

View File

@@ -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" />

View File

@@ -1,37 +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, width, height }: ImageData,
{ top, right, bottom, left }: Options,
): Promise<ImageData> {
const newWidth = width - left - right;
const newHeight = height - top - bottom;
const cols = width * 4;
const newCols = newWidth * 4;
const pixels = new Uint8ClampedArray(data.buffer, 0, newHeight * newCols);
for (let y = 0; y < newHeight; y++) {
const x = left * 4;
const row = new Uint8ClampedArray(
data.buffer,
(top + y) * cols + x,
newCols,
);
pixels.set(row, y * newCols);
}
return new ImageData(pixels.slice(), newWidth, newHeight);
}

View File

@@ -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" />

View File

@@ -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,
};

View File

@@ -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" />

View File

@@ -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);
}

View File

@@ -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" />

View File

@@ -273,7 +273,7 @@ export default class Intro extends Component<Props, State> {
)}
<div
class={style.loadImgContent}
style={{ visibility: __PRERENDER__ ? 'hidden' : undefined }}
style={{ visibility: __PRERENDER__ ? 'hidden' : '' }}
>
<button class={style.loadBtn} onClick={this.onOpenClick}>
<svg viewBox="0 0 24 24" class={style.loadIcon}>

View File

@@ -11,7 +11,6 @@ html {
--less-light-gray: #bcbcbc;
--medium-light-gray: #d1d1d1;
--light-gray: #eaeaea;
--med-gray: #555;
--dark-gray: #333;
--dim-text: #343a3e;
--dark-text: #142630;