Giter Club home page Giter Club logo

jsquash's Introduction

jSquash 🥝

Collection of WebAssembly image codecs that support the browser and are derived from the Squoosh App

The aim of this library is to provide an easy experience to encode, decode and modify images with the tools you know and love from Squoosh in the Browser and Web Worker environments.

jSquash name is inspired by jQuery and Squoosh. It symbolizes the browser support focus of these packages.

⚠️ There is limited support for Node.js environments. The experimental Node.js support is provided for convenience and is not the primary focus of this project. For much better Node based alternatives please check out the Squoosh Node.js library and Sharp.

Differences with Squoosh

  • The codecs and tools are built for both Web and Web Worker environments
  • No dynamic code execution, the packages can be run in strict environments that do not allow code evaluation. Like Cloudflare Workers.
  • Does not rely on TextEncoder/TextDecoder API (could reduce performance) but allows it to be run in simpler V8 runtimes that only support UTF-8 (Cloudflare Workers, Vercel Edge Functions etc.)

Packages

⚠️ All packages are ESM modules. You may need to manually transpile the packages if your build environment still relies on Commonjs formats.

Usage in the Browser

You can use the packages directly from the Unpkg CDN and can be the easiest way to get started.

import { decode } from "https://unpkg.com/@jsquash/jpeg?module";
import { encode } from "https://unpkg.com/@jsquash/webp?module";

const imageResponse = await fetch("https://picsum.photos/200/300.jpg");
const imageData = await decode(await imageResponse.arrayBuffer());
const webpImageBuffer = await encode(imageData);

To target a specific version, you can use the @version syntax.

import { encode } from "https://unpkg.com/@jsquash/[email protected]?module";

Checkout the with CDN example for a working demo.

Usage in Node.js

Using jSquash modules with Node.js requires some additional steps so that the WASM binaries can be included. The support is limited and the WASM modules are not optimized for speed with Node.js.

Check out the with Node.js example for a working demo.

Usage in Cloudflare Workers

Using jSquash modules with Cloudflare Workers requires some additional steps so that the WASM binaries get included.

Depending on which format you are using check the examples below:

Other Examples

Known Issues

Issues with Vite and Vue build environments

This may present itself as any of the following errors:

  • TypeError: Failed to construct 'URL': Invalid URL
  • RuntimeError: Aborted(both async and sync fetching of the wasm failed). Build with -sASSERTIONS for more info.
  • Other console errors could also be related to this issue

As a workaround, update your vite.config.js file with the optimizeDeps property. Put affected module names in the exclude array. Vites dependency optimizer seems to be causing issues with the WASM modules.

import { defineConfig } from 'vite'

export default defineConfig({
  optimizeDeps: {
    exclude: ["@jsquash/png"]
  }
})

Issues with Nuxt build environments

This may present itself as a Cannot find module error. This is likely because Nuxt is anticipating third party modules to be in the Commonjs format.

Setting the following Nuxt config with the jSquash packages that your app uses seems to resolve it.

export default defineNuxtConfig({
  build: {
    transpile: ["@jsquash/png"],
  },
  vite: {
    optimizeDeps: {
      exclude: ["@jsquash/png"],
    },
  },
});

Issues with Nuxt/Vite and nested Web Workers

There is a known Vite bug breaking production code compilation when using a worker that references another worker, see issue #19 for more information.

Unexpected early exit. This happens when Promises returned by plugins cannot resolve. Unfinished hook action(s) on exit:

In the meantime, you can install special builds that don't use workers to work around this issue:

jsquash's People

Contributors

dependabot[bot] avatar jamsinclair avatar rovinglight avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

jsquash's Issues

Fails to build with typescript when skipLibCheck is false

Firstly just wanted to say thanks for the library, works great!

Vite: 2.8.2
Typescript: 4.5.5

"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitThis": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,

This typescript issue looks to be directly related to: GoogleChromeLabs/squoosh#1219

error TS2503: Cannot find namespace 'EmscriptenWasm'.
export interface MozJPEGModule extends EmscriptenWasm.Module {
error TS2503: Cannot find namespace 'EmscriptenWasm'.
declare var moduleFactory: EmscriptenWasm.ModuleFactory<MozJPEGModule>;
error TS2748: Cannot access ambient const enums when the '--isolatedModules' flag is provided.
export { EncodeOptions, MozJpegColorSpace };

Tried directly installing types for Emscripten but didn't work.

For now we have simply enabled skipLibCheck but thought this would be a good one to keep track of.

Basic Node.js Support

The library was intentionally built for the browser and web browsers.

Theoretically Node.js support should be possible, although I won't go out of my way to make this a core feature.

The main blockers I have found from experimentation:

  • Imports need .js file extensions so they can be resolved by default node module system strategy
  • Need to polyfill ImageData
  • Need to manually read wasm binaries and compile to Wasm modules

Module not found

Describe the bug

Getting a Module not found: Can't resolve 'https://unpkg.com/@jsquash/jpeg?module'

To Reproduce

Just the very basic - added an import statement as given here https://github.com/jamsinclair/jSquash?tab=readme-ov-file#usage-in-the-browser

Environment

React 17 + CRA 4 + Craco with config:

module.exports = {
  webpack: {
    configure: {
      module: {
        rules: [
          {
            type: 'javascript/auto',
            test: /\.mjs$/,
            include: /node_modules/,
          },
        ],
      },
    },
  },
}


Happy to provide any further info. I find this part about the JS toolchaining quite confusing, and my knowledge in this area is poor.

Support cjs and umd in packages

Currently the published NPM modules only expose esm modules. To increase compatibility with other developer environments we could also build valid cjs and umd bundles.

Should we? 🤔

Nuxt build error: UMD and IIFE output formats are not supported for code-splitting builds.

Describe the bug
Using with nuxt 3 works in dev environment, but breaks during the production build

Error: Nuxt Build Error: [commonjs--resolver] Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.

To Reproduce
Steps to reproduce the behavior:

  1. Create a new nuxt project and install any of the jsquash codecs
  2. Handle the transpiling in nuxt config
build: {
    transpile: [
      "@jsquash/png",
      "@jsquash/avif",
      "@jsquash/jpeg",
      "@jsquash/jxl",
      "@jsquash/oxipng",
      "@jsquash/png",
      "@jsquash/webp",
    ],
  },
  vite: {
    optimizeDeps: {
      exclude: [
        "@jsquash/avif",
        "@jsquash/jpeg",
        "@jsquash/jxl",
        "@jsquash/oxipng",
        "@jsquash/png",
        "@jsquash/webp",
      ],
    },
  },
  1. run npm run build
  2. See error

Improve @jquash/avif decode speed

Decode speed is about 1200ms - 1500ms, but when I use an AVIF supported browser, speed is about 200ms - 300ms. Is there any way to increase decoding speed?

Issues importing the library in a Nuxt3/Vue3/Vite environment

Hi,

This might be a very basic issue that I'm simply unable to solve (probably due to lack of knowledge of how wasm works). I'm trying to import the modules into my Vue App in order to encode AVIF files and i'm getting the following error

500 Cannot find module 'C:\Users\cab_l\Desktop\Repositories\wsp-3.0\node_modules\@jsquash\avif\encode' imported from C:\Users\cab_l\Desktop\Repositories\wsp-3.0\node_modules\@jsquash\avif\index.js

I did check the node_modules folder and the module is present thus I have no idea why that might be the case.

My code looks like this

./composables/useUtiles.ts

import { encode } from "@jsquash/avif";

  const convertToAvif = (file) => {
    return new Promise(async (res, rej) => {
      console.log("file ", file);
      const avifBuffer = await encode(file);
      console.log("buffer ", avifBuffer);
      res();
    });
  };

I'm guessing that the issue might be related to this step in the readme file that I'm failing to understand how to proceed with

Note: You will need to either manually include the wasm files from the codec directory or use a bundler like WebPack or Rollup to include them in your app/server.

Any help with this would be greatly appreciated

Document `init` methods

All the modules expose an init function that allows the WASM binaries to be initialized in a custom way. This is useful for environments like Cloudflare Workers where WASM binaries are injected as global vars.

We lack written documentation for this. We should add info for this to all module READMEs.

Got URL TypeError while using Vite2 to start dev server

I'm using latest Vue3+Vite2, this package is pretty good (I successfully tested it on production build).

Description

My codes:

import { encode as EncodePNG, decode as DecodePNG } from "@jsquash/png";
import { encode as EncodeJPG, decode as DecodeJPG } from "@jsquash/jpeg";
import { encode as EncodeWebP, decode as DecodeWebP } from "@jsquash/webp";
import { encode as EncodeAVIF, decode as DecodeAVIF } from "@jsquash/avif";
let data = await DecodeJPG(await file.arrayBuffer());

When I test it on Dev server, I got a TypeError for URL:

mozjpeg_dec.js:9 Uncaught (in promise) TypeError: Failed to construct 'URL': Invalid URL
    at mozjpeg_dec.js:9
    at initEmscriptenModule (utils.js:25)
    at init2 (decode.js:17)
    at decode (decode.js:21)
    at HTMLInputElement.handleFile (Test.vue:22)

located at:

var wasmBinaryFile = new URL("mozjpeg_dec.wasm",import.meta.url).toString()

After I build it by npm run build and test this on production build, everything works as normal.
I believe this issue is related to Vite's esbuild module or another, but I can make sure what caused this.

How to produce

  1. create a new Vite project by npm init vite@latest
  2. import this package and call its function
  3. run dev server by npm run dev
  4. test it

Thanks!

Create a reference decode speed benchmark page

Inspiration came from recent references to decode speeds in #44 and #43.

We could likely create a simple benchmark script and HTML page that renders the results. This library is not focused on speed, but it would be convenient to compare speeds between decoding with browser image and canvas APIs vs the WASM decode speed.

Oxipng multi-threaded mode fails when run from main thread.

Describe the bug
Oxipng multi threaded code doesn't work when not wrapped in a worker.

This happens due to a known issue with threading solutions with wasm and rust (https://github.com/GoogleChromeLabs/wasm-bindgen-rayon#caveats).

To Reproduce
Steps to reproduce the behavior:

  1. Copy the with-vite example
  2. Modify the code to import and use oxipng in the main js script
  3. Set in the development server the following headers:
headers: {
  "Cross-Origin-Opener-Policy": "same-origin",
  "Cross-Origin-Embedder-Policy": "require-corp",
}

Expected behavior

We should update our wrapper code to not run multi-threaded code unless in a dedicated worker context.

Simple CDN example

It'd be great if there were a simple copy+paste CDN example in the readme. This would allow people to easily try it out in the browser, deno, etc. with a simple copy-paste code snippet. I tried this:

let webp = await import("https://cdn.jsdelivr.net/npm/@jsquash/[email protected]/+esm");

and it loads, but when I try decoding:

await webp.decode(arrayBuffer)

it can't find the codec files. I saw this example in the readme:

import decode, { init as initWebpDecode } from '@jsquash/webp/decode';

initWebpDecode(WASM_MODULE); // The `WASM_MODULE` variable will need to be sourced by yourself and passed as an ArrayBuffer.
const image = await fetch('./image.webp').then(res => res.arrayBuffer()).then(decode);

But https://cdn.jsdelivr.net/npm/@jsquash/webp/[email protected]/+esm doesn't exist, and @jsquash/webp only seems to export decode and encode functions - nothing else.

I also tried esm.sh: https://esm.sh/@jsquash/[email protected] but it isn't even able to load the module in the first place, for whatever reason.

Add helpers package

Add light utilities for working with Array buffers, typed arrays and converting data to base64 strings.

Passing a custom fetch function

Is your feature request related to a problem? Please describe.
When trying initialize a codec (WebAssembly Module) in SvelteKit it throws an error:

Error: Cannot use relative URL (/codecs/mozjpeg_dec_codec.wasm) with global fetch — use event.fetch instead: https://kit.svelte.dev/docs/web-standards#fetch-apis

Describe the solution you'd like
Make it possible to add a custom fetch function to the module options:

import decodeJpeg, { init } from '@jsquash/jpeg/decode';

export const load = async ({ fetch }) => {
    await init(undefined, {
        fetch,
        locateFile: () => '/codecs/mozjpeg_dec_codec.wasm'
    });
}

Describe alternatives you've considered
None

Additional context
I think this works as a workaround:

import JPEG_DEC_WASM from '@jsquash/jpeg/codec/dec/mozjpeg_dec.wasm?module';

export const load = async ({ fetch }) => {
    const response = await fetch(JPEG_DEC_WASM);
    const wasmArrayBuffer = await response.arrayBuffer();
    const wasmModule = new WebAssembly.Module(wasmArrayBuffer);

    await init(wasmModule);
    
    const image = await fetch('/dog.jpg')
	.then((res) => res.arrayBuffer())
	.then(decodeJpeg);
}

Doesn't work in Vite SSR

Describe the bug
With Vite SSR first thing that you encounter is that you can't import jSquosh at all, it fails with:

node:internal/errors:496
    ErrorCaptureStackTrace(err);
    ^

Error: Cannot find module '/home/user/jSquash/examples/with-vite/node_modules/@jsquash/webp/codec/dec/webp_dec' imported from /home/user/jSquash/examples/with-vite/node_modules/@jsquash/webp/decode.js
    at new NodeError (node:internal/errors:405:5)
    at finalizeResolution (node:internal/modules/esm/resolve:224:11)
    at moduleResolve (node:internal/modules/esm/resolve:836:10)
    at defaultResolve (node:internal/modules/esm/resolve:1034:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:375:12)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:344:25)
    at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:220:38)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39)
    at link (node:internal/modules/esm/module_job:84:36) {
  url: URL {},
  code: 'ERR_MODULE_NOT_FOUND'
}

Importing using relative paths worked.
optimizeDeps.exclude didn't help in any way.

OK, than with helpful suggestion of the people from the Astro chat I found that adding jSquash to ssr.noExternal fixed that.

But I couldn't fix the following:
On Node 16 and Bun during decoding it fails with:

RuntimeError: Aborted(both async and sync fetching of the wasm failed). Build with -sASSERTIONS for more info.

On Node 20 it fails with:

TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:11600:11) {
  cause: Error: not implemented... yet...
      at makeNetworkError (node:internal/deps/undici/undici:6911:35)
      at schemeFetch (node:internal/deps/undici/undici:11050:18)
      at node:internal/deps/undici/undici:10930:26
      at mainFetch (node:internal/deps/undici/undici:10947:11)
      at fetching (node:internal/deps/undici/undici:10904:7)
      at fetch2 (node:internal/deps/undici/undici:10782:20)
      at Object.fetch (node:internal/deps/undici/undici:11598:18)
      at fetch (node:internal/process/pre_execution:274:25)
      at instantiateAsync (/home/user/jSquash/examples/with-vite/node_modules/@jsquash/webp/codec/dec/webp_dec.js:8:8065)
      at createWasm (/home/user/jSquash/examples/with-vite/node_modules/@jsquash/webp/codec/dec/webp_dec.js:8:9070)
}

As I know Vite has knows problems with fetch during SSR, hence I tried importing it manually.

import webpDecode, { init as initWebpWasm } from "@jsquash/webp/decode";
const wasm = await readFile("./node_modules/@jsquash/webp/codec/dec/webp_dec.wasm");
await initWebpWasm(new WebAssembly.Module(wasm));
const webp = { decode: webpDecode };

(this code is also commented out in the stackblitz reproduction example)

But now there is this error:
Node 20:

TypeError: Cannot read properties of undefined (reading 'bind')
    at /home/user/jSquash/examples/with-vite/node_modules/@jsquash/webp/codec/dec/webp_dec.js:8:30970
    at __emval_new (/home/user/jSquash/examples/with-vite/node_modules/@jsquash/webp/codec/dec/webp_dec.js:8:31249)
    at null.<anonymous> (wasm://wasm/00086ba2:1:122608)
    at null.<anonymous> (wasm://wasm/00086ba2:1:124983)
    at Object.decode (/home/user/jSquash/examples/with-vite/node_modules/@jsquash/webp/codec/dec/webp_dec.js:8:18298)
    at Object.decode (/home/user/jSquash/examples/with-vite/node_modules/@jsquash/webp/decode.js:23:27)
    at /home/user/jSquash/examples/with-vite/test.ts:13:13
    at ViteNodeRunner.runModule (file:///home/user/jSquash/examples/with-vite/node_modules/vite-node/dist/client.mjs:342:5)
    at ViteNodeRunner.directRequest (file:///home/user/jSquash/examples/with-vite/node_modules/vite-node/dist/client.mjs:326:5)
    at ViteNodeRunner.cachedRequest (file:///home/user/jSquash/examples/with-vite/node_modules/vite-node/dist/client.mjs:189:14)

Bun:

TypeError: undefined is not an object (evaluating 'constructor.bind')
      at WASM  ([wasm code])
      at WASM  ([wasm code])
      at processTicksAndRejections (:1:2602)

Vite doesn't support importing wasm same way as in the cloudflare worker examples, there is a plugin to fix that, but it doesn't work.
Is my API usage valid at all?

To Reproduce
Steps to reproduce the behavior:

  1. Try to use jSquash in vite-node environment
  2. ???
  3. Error

Reproduction URL
https://stackblitz.com/edit/vitejs-vite-ndaywm?file=main.js

Module Versions
Latest of them all

Additional context
I was trying to create an image service for Astro, but was blocked by this.
I found out that vite-node uses SSR too, hence it is used here for reproduction as it is much simpler than using framework framework framework.
Just noting that it worked in Astro during astro dev, the problem there is with astro build.

Add examples

  • Add Webpack example
  • Add Rollup example
  • Add Cloudflare worker example
  • Add Web Worker example

jpeg and webp fail to import inside web worker

Describe the bug
When I try to import jpeg or webp inside a web worker, I get this error:

⨯ ./node_modules/@jsquash/jpeg/codec/enc/mozjpeg_enc.js
Module parse failed: Identifier 'Module' has already been declared (5:12)
|     return function() {
|         let Module = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
>         var Module = typeof Module != "undefined" ? Module : {};
|         var readyPromiseResolve, readyPromiseReject;
|         Module["ready"] = new Promise(function(resolve, reject) {

avif and png both work though.

Solution
I noticed that avif and png use a slightly different syntax in the enc/dec files and they don't have this problem. I can work on a PR if preferred, or you can just change this inside jpeg/codec/enc/mozjpeg_enc.js, jpeg/codec/dec/mozjpeg_dec.js, webp/codec/enc/webp_enc.js, and webp/codec/dec/webp_dec.js to match the syntax used in avif and png that should fix it. I tried changing them locally and it fixed the problem for me.

Change

var Module = (() => {
  var _scriptDir = import.meta.url;
  
  return (
function(Module = {})  {

to

var Module = (function() {
  var _scriptDir = import.meta.url;
  
  return (
function(Module) {
  Module = Module || {};

Thanks.

Possibility of converting an image to AVIF without losing quality

Hi,

I was wondering if there is any way of converting an image without losing quality. I've tried tweaking with the encode options to no avail(mostly the cqLevel). It's mostly visible when i convert low rez images, and less so when using high rez images. These are the current options I'm now using

cqLevel: 10, cqAlphaLevel: -1, denoiseLevel: 0, tileColsLog2: 0, tileRowsLog2: 0, speed: 6, subsample: 1, chromaDeltaQ: false, sharpness: 0,

Cannot instantiate avif wasm module manually

When darting through the code recently I think I observed a potential bug that would stop people from manually instantiating a wasm module. People using this in a CloudFlare Worker would likely be impacted.

export async function init(module?: WebAssembly.Module) {
if (await threads()) {
const avifEncoder = await import('./codec/enc/avif_enc_mt');
return initEmscriptenModule(avifEncoder.default, module);

Notice we never set the emscriptenModule with the passed module. This would prevent us from storing this module in memory.

WebP Encoder Init Issue

In the WebP encoder the following is used in the init functions to detect SIMD support:

export async function init(module?: WebAssembly.Module): Promise<WebPModule> {
  if (await simd()) {
    const webpEncoder = await import('./codec/enc/webp_enc_simd');
    emscriptenModule = initEmscriptenModule(webpEncoder.default, module);
    return emscriptenModule;
  }
  const webpEncoder = await import('./codec/enc/webp_enc');
  emscriptenModule = initEmscriptenModule(webpEncoder.default, module);
  return emscriptenModule;
}

This causes the following error in Cloudflare workers since dynamic imports do not work:

TypeError: Invalid URL
    at new NodeError (node:internal/errors:371:5)
    at onParseError (node:internal/url:552:9)
    at new URL (node:internal/url:628:5)
    at /Users/.../node_modules/@jsquash/webp/codec/enc/webp_enc_simd.js:9:8622
    ...
    at callRouteLoader (/Users/.../node_modules/@remix-run/server-runtime/esm/data.js:69:14) {
  input: 'webp_enc_simd.wasm',
  code: 'ERR_INVALID_URL'
}

It would be nice if the init function could be modified to something along the lines of:

import webp_enc from './codec/dec/webp_enc';

export async function init(module?: WebAssembly.Module, simdModule?: WebAssembly.Module): Promise<WebPModule> {
  if (await simd()) {
    const webpEncoder = simdModule ? simdModule : await import('./codec/enc/webp_enc_simd');
    emscriptenModule = initEmscriptenModule(webpEncoder.default, module);
    return emscriptenModule;
  }

  emscriptenModule = initEmscriptenModule(webp_enc, module);
  return emscriptenModule;
}

This would allow the choice of opting-in/out of SIMD and support the Cloudflare environment by passing the SIMD module from the environment.

@jsquash/png fails to decode some images

Describe the bug
Given some pngs, jsquash fails to decode and throws an error.

To Reproduce
Steps to reproduce the behavior:

  1. Download broken.zip and unzip broken.png out of it
  2. Load broken.png's buffer
  3. call decode() on broken.png buffer

Reproduction
In this reproduction, there is 2 images, working.png and broken.png. The reproduction loads each image and attempts to decode it.

Expected behavior

decoding ./src/broken.png
    success!

Actual behavior

decoding ./src/broken.png
error: Uncaught (in promise) Error: `unwrap_throw` failed
    throw new Error(getStringFromWasm0(arg0, arg1));
          ^
    at imports.wbg.__wbindgen_throw (https://unpkg.com/@jsquash/[email protected]/codec/squoosh_png.js?module:157:11)
    at <anonymous> (https://unpkg.com/@jsquash/[email protected]/codec/squoosh_png_bg.wasm:1:91616)
    at <anonymous> (https://unpkg.com/@jsquash/[email protected]/codec/squoosh_png_bg.wasm:1:40587)
    at decode (https://unpkg.com/@jsquash/[email protected]/codec/squoosh_png.js?module:109:18)
    at decode (https://unpkg.com/@jsquash/[email protected]/decode.js?module:23:27)
    at eventLoopTick (ext:core/01_core.js:181:11)
    at async tryDecode (file:///workspace/src/index.ts:6:21)
    at async file:///workspace/src/index.ts:13:1

Module Versions
@jsquash/png 2.2.0 (latest)

Additional Context
I have no reason to believe the broken.png in question has problematic encoding, as it functions perfectly with the following:

  1. Paint.NET
  2. Windows Photo viewer
  3. Discord's upload
  4. Github's upload
  5. Minecraft resource pack

Update OxiPNG source

The last publish of @jsquash/oxipng was 11/18/2021, since then OxiPNG has undergone 3 major updates, including several optimization performance improvements.

Also please add alpha optimizations to OptimiseOptions

Cannot find module in sveltekit project

Describe the bug
The module cannot find it's internal files, should be vite related problem but not sure.

To Reproduce
Steps to reproduce the behavior:

  1. pnpm create svelte
  2. Install @jsquash/png
  3. Exclude optimization in vite.config.ts
  4. import any @jsquash modules e.g. @jsquash/png
  5. pnpm dev
  6. See error

Reproduction URL
https://codesandbox.io/p/github/lknlknim/sveltekit-jsquash/main

Expected behavior
No error

Module Versions
2.1.4

Additional context
The import was located in /src/routes/image/+server.ts

Tried both npm / yarn / pnpm, all giving the same error.
Even copy the module folder to local folder won't work

What I was trying to achieve is to make it available on Cloudflare Pages functions(Cloudflare Worker) with Sveltekit to get rid of Cloudflare Image and Cloudflare Image Resize.

Resize component memory leak

The resize component may have a memory leak. I have tried executing it consecutively in both the browser and Deno environment, and the memory usage rapidly increases without being reclaimed.

Another issue is with the PNG decoder, which consistently encounters the error "Failed to construct 'ImageData': The input data length is not a multiple of 4." for certain images. This requires relying on the browser's canvas for PNG decoding (which works fine in the browser), but the problem arises when running on the server-side in Deno where a usable canvas is not available.

background execution?

Describe the bug
My page freezes when this is going. Is there a way to make this run on a background thread? I'm using the vite version.

Edit: just switched to the regular, and it still freezes (the spinner stops spinning) while converting

To Reproduce
Steps to reproduce the behavior:
just use it as usual

Export single thread only methods for codecs that have multi-threading.

Is your feature request related to a problem? Please describe.

There is a bug in Vite that prevents the compilation of multi-threaded code in the codecs (See #19). This may take a long time to be fixed.

In the interim the codecs can not be easily used in Vite and Nuxt projects without installing a separate package.

Describe the solution you'd like

The multi-threaded codecs, AVIF, JXL and oxipng could include a single-thread module that can be imported and be compatible with Vite and Nuxt Projects.

E.g.

import { optimise } from '@jsquash/oxipng/single-thread';

Describe alternatives you've considered
N/A

Polyfill ImageData when using png module in Cloudflare Worker environment

Is your feature request related to a problem? Please describe.

The png module is not easy to use with Cloudflare Workers and fails unless additional polyfills are added.

Other jSquash modules polyfill useful APIs like ImageData like jpeg, avif and jxl.

Describe the solution you'd like

The png module polyfills ImageData when used in a Cloudflare Worker

Additional context
Originally requested in #21 (comment)

Failed to work in cloudflare workers esm mode

// @ts-ignore
import PNG_DEC_WASM from '../../node_modules/@jsquash/png/codec/squoosh_png_bg.wasm';
// @ts-ignore
import WEBP_ENC_WASM from '../../node_modules/@jsquash/webp/codec/enc/webp_enc_simd.wasm';

await initPngWasm(PNG_DEC_WASM);
const imageData = await decodePng(buffer);

await initWebpWasm(WEBP_ENC_WASM);
const webpBuffer = await encodeWebp(imageData); // error happens here

error:

TypeError: Cannot read properties of undefined (reading 'href')
    at index.js:369:45
    at initEmscriptenModule (index.js:3278:10)
    at init (index.js:3293:24)
    at async encodeImageToWebp (index.js:3454:25)
    at async index.js:3504:26
    at async handle (index.js:86:34)
    at async Object.handle (index.js:86:34) {
  stack: TypeError: Cannot read properties of undefined (re…6:34)
    at async Object.handle (index.js:86:34),
  message: Cannot read properties of undefined (reading 'href')
}

After some investigating, this may be related to https://github.com/jamsinclair/jSquash/blob/1edfc086e22b6aa01910cff5fd20826cf9e3dfa2/packages/webp/codec/enc/webp_enc.js which access this.location.href

Provide way to override WASM file location

Is your feature request related to a problem? Please describe.

Due to server restrictions in my project, it's not possible to load the WASM file from the same location as the JS is loaded.

Describe the solution you'd like

Providing a way to override the WASM file location by specifying a custom locateFile would help resolve my use case.

Describe alternatives you've considered

Right now I'm simply patching this package

diff --git a/node_modules/@jsquash/jpeg/encode.d.ts b/node_modules/@jsquash/jpeg/encode.d.ts
index 740244c..7d78f20 100644
--- a/node_modules/@jsquash/jpeg/encode.d.ts
+++ b/node_modules/@jsquash/jpeg/encode.d.ts
@@ -16,6 +16,6 @@
  * The jpeg options are defaulted to defaults from the meta.ts file.
  */
 import type { EncodeOptions } from './meta';
-export declare function init(module?: WebAssembly.Module): Promise<void>;
+export declare function init(module?: WebAssembly.Module, args?: Record<string, unknown>): Promise<void>;
 export default function encode(data: ImageData, options?: Partial<EncodeOptions>): Promise<ArrayBuffer>;
 //# sourceMappingURL=encode.d.ts.map
diff --git a/node_modules/@jsquash/jpeg/encode.js b/node_modules/@jsquash/jpeg/encode.js
index a6c65e8..3f90d3e 100644
--- a/node_modules/@jsquash/jpeg/encode.js
+++ b/node_modules/@jsquash/jpeg/encode.js
@@ -14,8 +14,8 @@ import mozjpeg_enc from './codec/enc/mozjpeg_enc';
 import { defaultOptions } from './meta';
 import { initEmscriptenModule } from './utils';
 let emscriptenModule;
-export async function init(module) {
-    emscriptenModule = initEmscriptenModule(mozjpeg_enc, module);
+export async function init(module, args) {
+    emscriptenModule = initEmscriptenModule(mozjpeg_enc, module, args);
 }
 export default async function encode(data, options = {}) {
     if (!emscriptenModule)
diff --git a/node_modules/@jsquash/jpeg/utils.js b/node_modules/@jsquash/jpeg/utils.js
index a00acf2..fd9927f 100644
--- a/node_modules/@jsquash/jpeg/utils.js
+++ b/node_modules/@jsquash/jpeg/utils.js
@@ -13,7 +13,7 @@
 /**
  * Notice: I (Jamie Sinclair) have modified this file to allow manual instantiation of the Wasm Module.
  */
-export function initEmscriptenModule(moduleFactory, wasmModule) {
+export function initEmscriptenModule(moduleFactory, wasmModule, args) {
     let instantiateWasm;
     if (wasmModule) {
         instantiateWasm = (imports, callback) => {
@@ -25,6 +25,7 @@ export function initEmscriptenModule(moduleFactory, wasmModule) {
     return moduleFactory({
         // Just to be safe, don't automatically invoke any wasm functions
         noInitialRun: true,
-        instantiateWasm
+        instantiateWasm,
+		...args,
     });
 }

This way I can do:

import { default as encodeJpeg, init as initJpeg } from '@jsquash/jpeg/encode';

await initJpeg( undefined, {
	locateFile: () =>
		'https://cdn.jsdelivr.net/npm/@jsquash/[email protected]/codec/enc/mozjpeg_enc.wasm',
} );

const buffer = await encodeJpeg( imageData );

Hacky, but works

Additional context

Allowing to override locateFile is common in similar projects such as mediainfo.js or ffmpeg.wasm.

Deno fails with "NotSupported: Classic workers are not supported."

Describe the bug
Using a multi-threaded supported module fails with:

NotSupported: Classic workers are not supported.

Reproduction

import encode from '@jsquash/avif/encode';

await encode(new ImageData(new Uint8ClampedArray(4 * 50 * 50), 50, 50));

Expected behavior
Should work without additional configuration.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.