Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No paths work when reading files (Deno) #59

Open
jtoppine opened this issue Dec 9, 2023 · 2 comments
Open

No paths work when reading files (Deno) #59

jtoppine opened this issue Dec 9, 2023 · 2 comments
Labels
question Further information is requested

Comments

@jtoppine
Copy link

jtoppine commented Dec 9, 2023

In Deno, vips.Image.newFromBuffer(buffer) works well! But opening local files directly for example vips.Image.newFromFile('sample.jpg') always complains:

error: vips::Error: unable to load from file sample.jpg
VipsForeignLoad: file "sample.jpg" does not exist

I thought it would be an issue of relative paths, but I tried all kinds of absolute and relative paths (relative to Deno.cwd(), or import.meta.url, or the directory where vips-es6.js is stored), with and without file:// prefix, with no luck.

Furthermore I tried a simple filename without path, and put a sample.jpg in every directory I could think of where it might try to load it. No luck.

Am I missing something, or is this is a bug with wasm-vips on deno?

Deno 1.38.5, wasm-vips version is i guess 0.0.7 (or whatever it is that jsdelivr is giving - I manually downloaded vips-es6.js and all the other JS and WASM file from jsdelivr in order to install and import locally since it seems using jsdelivr the wasm files are loaded again over the net every run? - could we perhaps somehow have these zipped on new releases?)

PS:

I'm running on VPS's with limited RAM, so while ArrayBuffers work, I'm hoping passing file paths directly would negate the need for allocating big in and out buffers, and perhaps allow vips to process data in a more streaming manner (if it does that, or does it need to load the full file data in memory?).

Ideally for Deno, it would be great if we were able to work with ReadableStream in and WritableStream out, but that does not appear possible at the moment. That's why I'm looking into passing file paths instead of ArrayBuffer

@kleisauke kleisauke added the question Further information is requested label Dec 26, 2023
@kleisauke
Copy link
Owner

Sorry, this dropped off my radar. Currently, wasm-vips doesn't support native file system access in environments other than Node.js, mainly because these environments typically reuses the web ES6 module. Emscripten issue emscripten-core/emscripten#12203 might be relevant here.

On the web, the whole filesystem is virtualized (see MEMFS). So, you need to preload the file first, for example on Deno:

import Vips from 'https://cdn.jsdelivr.net/npm/wasm-vips/lib/vips-es6.js';

const vips = await Vips({
  // Optimize startup time by disabling the dynamic modules
  dynamicLibraries: [],
  preRun: async (module) => {
    // Preload banana.webp
    module.FS.createDataFile('/', 'banana.webp', await Deno.readFile('playground/src/images/banana.webp'), true, false);
  }
});

// Load an image from a preloaded file
const im = vips.Image.newFromFile('banana.webp[n=-1]', {
  access: vips.Access.sequential // 'sequential'
});

// Write the result to a GIF file
const outBuffer = im.writeToBuffer('.gif');
await Deno.writeFile('banana.gif', outBuffer);

// Memory management
im.delete();

// Exit the Deno runtime (i.e. a more "commandline" experience)
Deno.exit();
$ deno run --allow-net --allow-read --allow-write test.js

it seems using jsdelivr the wasm files are loaded again over the net every run?

IIUC, Deno reuses a module from its cache without having to fetch it again on subsequent runs, but I'm not sure how Deno manages Wasm files internally.

could we perhaps somehow have these zipped on new releases?

wasm-vips will only be distributed via npmjs.com; there are currently no plans to distribute it through other channels.

allow vips to process data in a more streaming manner

I think the source/target API of libvips would be suitable for this.
https://www.libvips.org/2019/11/29/True-streaming-for-libvips.html

it('custom', function () {
const stream = vips.FS.open(Helpers.jpegFile, 'r');
const source = new vips.SourceCustom();
source.onRead = (ptr, size) =>
BigInt(vips.FS.read(stream, vips.HEAPU8, ptr, vips.bigintToI53Checked(size)));
source.onSeek = (offset, whence) =>
BigInt(vips.FS.llseek(stream, vips.bigintToI53Checked(offset), whence));
const image = vips.Image.newFromSource(source, '', {
access: 'sequential'
});
const image2 = vips.Image.newFromFile(Helpers.jpegFile, {
access: 'sequential'
});
expect(image.subtract(image2).abs().max()).to.equal(0);
vips.FS.close(stream);
});

it would be great if we were able to work with ReadableStream in and WritableStream out

Indeed, this would be a great addition. It can probably be implemented using a new pair of Image.newFromStream() / image.writeToStream() functions.

@jtoppine
Copy link
Author

Thank you, lot of valuable information there. And no worries. I ended up interfacing with native vips cli (for now), and turned out working very well. Still interested in using wasm-vips sometime in the future, though!

I was wondering what types the Source & Target related methods were referring to. Indeed it looks like one could use that kind of lower level interface to make an adapter that would work with almost any type of IO access, including ReadableStream/WritableStream. Feels like a nice project idea for a deno.land/x module, a wrapper of sorts that would feel more "deno native". Might even take up the challenge sometime in the future, no promises though!

On the other hand, having newFromStream/writeToStream methods built into wasm-vips might make sense outside of deno too, since them being standard web apis, could be useful in some purely client side browser apps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants