silvia-odwyer / photon

⚡ Rust/WebAssembly image processing library
https://silvia-odwyer.github.io/photon
Apache License 2.0
2.72k stars 154 forks source link

RejectionError: unreachable on Call to photon.PhotonImage.new_from_base64 (WASM-Node) #45

Open Saeris opened 4 years ago

Saeris commented 4 years ago

Hey there! First off, really cool project, it appears to be exactly what I'm looking for. Bad news however is that I'm having difficulty getting it to work. On attempting to create a new PhotonImage in Node, I'm encountering a bunch of wasm errors that I cannot decipher. Here's the console output from my test:

 FAIL   isomorphic-vibrant  src/__TEST__/vibrant.spec.ts
  vibrant
    ✕ generates a palette (1308 ms)

  ● vibrant › generates a palette

    RuntimeError: unreachable
        at wasm-function[1200]:26
        at wasm-function[624]:286
        at wasm-function[888]:116
        at wasm-function[1153]:45
        at wasm-function[871]:126
        at wasm-function[670]:212
        at wasm-function[930]:47
        at wasm-function[863]:38
        at wasm-function[147]:1104
        at wasm-function[36]:4639
        at wasm-function[543]:23
        at wasm-function[39]:2496
        at wasm-function[477]:68
        at wasm-function[797]:18

      25 |     const b64 = await convertBlobToBase64(blob)
      26 |     const str = b64.replace(/^data:.*\/.*;base64,/, ``)
    > 27 |     this.#image = photon.PhotonImage.new_from_base64(str)
         |                                      ^
      28 |     return this
      29 |   }
      30 |

      at Function.new_from_base64 (node_modules/@silvia-odwyer/photon-node/photon.js:2561:24)
      at NodeImage [as load] (src/image.ts:27:38)

My goal here is to use Photon to aid in extracting the prominent colors from an image, and I would like it to work universally across node, the browser, and web workers. The reason for hitting those targets is that I'm mostly intending to use this in serverless environments, which have restrictions on the total size of compiled code and do not always have native node features (example, you can't use fs or http in Cloudflare Workers). Execution speed is also really important, which was what made using the project I forked this from (node-vibrant) prohibitive to use. WASM definitely seems the way to go.

You can check out the implementation in the CodeSandbox linked below. To reproduce the error, click the + button in the Terminal tab to open a new terminal, where you can then run yarn test the same as you would from your local machine. Here's a screenshot for reference since it can be a little hard to find:

https://codesandbox.io/s/github/Saeris/isomorphic-vibrant?file=/src/image.ts

image

I understand that currently image resizing is not available in the node package. For now I'm just trying to debug why I can't create a new PhotonImage from a valid base64 string. I'm clueless when it comes to Rust development, so I don't know if there's a way you could also make these errors more helpful.

silvia-odwyer commented 4 years ago

Hi @Saeris Thanks for opening this issue, I'm going to investigate this and try identify the cause of the error. I'll update you when I find a fix, hopefully very soon.

Really glad that you're using Photon for your library, and I'm looking forward to seeing the increased performance over node-vibrant, well, once we get it + photon working first 😄

silvia-odwyer commented 4 years ago

Hi @Saeris The issue appears to be because of the following line:

    const res = await fetch(this.#src.toString())

To get the correct base64, you need to set your response type to a buffer rather than a string, this is why invalid base64 was being generated and as a result Photon couldn't convert the base64 to an image. So it appears to be an issue with the way the resulting data is encoded.

I see you're using cross-fetch to fetch your images. I've coded up an example using node-fetch, so if you update Line 21 of your code to code similar to below, it should work:

await fetch(image_url)
    .then(data => data.buffer())
    .then(buffer => {
        let b64 = buffer.toString('base64'); 
        let photon_image = photon.PhotonImage.new_from_base64(b64);
        // photon.grayscale(photon_image);
    });

Hope that helps! And if you have any other questions or bug reports, do let me know 😄

Saeris commented 4 years ago

Hey @silvia-odwyer thanks for taking the time to look into this! I'll see if I can make something work of the solution you proposed. I'd like to point out that cross-fetch is a wrapper around node-fetch for a cross platform compatibility layer, so I shouldn't need to change my dependencies at all. If there is indeed an issue with using cross-fetch then some other solution will need to be found, as node-fetch by itself will pose compatibility problems with web-workers, as mentioned in #46. Since this is just a bump in the road to getting my library shipped, I'll come back with updates to ensure this issue has been solved.