Open christianecker-vil opened 4 years ago
Are you using the latest dev branch from GitHub? That has a fix for issue 468. I'll follow up to confirm the dataURL support.
Switch to the dev branch fixed the error but in my case (testing environment) not really solves it: When I write the dataURL image to a local image.png
file and create the Texture using the filename results in
ENOENT: no such file or directory, open 'http://localhost/image.png'
at Function.loadHeadlessBuffer (node_modules/@jonobr1/two.js/build/two.js:6062:23)
at Object.img (node_modules/@jonobr1/two.js/build/two.js:6162:19)
at Function.load (node_modules/@jonobr1/two.js/build/two.js:6236:30)
at Texture._update (node_modules/@jonobr1/two.js/build/two.js:6481:19)
at new Texture (node_modules/@jonobr1/two.js/build/two.js:6008:10)
When using Two.js in node, it uses the fs
module and loads images that way. Specifying URLs doesn't work. This is something Two.js should handle though.
I did not specify the URL, my code looks like:
fs.writeFile("./image.png", buf);
const texture = new Two.Texture("./image.png");
Hmm, in theory I feel like that should work. But, your error shows that your application is resolving "./image.png"
to http://localhost/image.png
. It's hard to help debug this issue without seeing more of your application... Perhaps you could try wrapping your path with the path.resolve
method from the native path
module in node like so:
const path = require("path");
const texture = new Two.Texture(path.resolve(__dirname, "./image.png"));
To unpack a little of what other issues lie here.
dataURI
to Two.js then you should be able to through a buffer.e.g:
var sprite = two.makeSprite(Buffer.from(dataURI, 'base64'), x, y);
const texture = new Two.Texture(path.resolve(__dirname, "./image.png"));
Have you tried this?
const texture = new Two.Texture("file://" + path.resolve(__dirname, "./image.png"));
Tried both but unfortunately the resulting paths don't work here:
const texture = new Two.Texture(path.resolve(__dirname, "./image.png"));
ENOENT: no such file or directory, open 'http://localhost/Users/.../image.png'
const texture = new Two.Texture("file://" + path.resolve(__dirname, "./image.png"));
ENOENT: no such file or directory, open 'file:///Users/../image.png'
I suppose that the comment here is still valid 😄
getAbsoluteURL: function(path) {
if (!anchor) {
// TODO: Fix for headless environments
return path;
}
anchor.href = path;
return anchor.href;
},
I'll reiterate, if you can share more of your application I'll be able to help out. The errors you're showing reveal that Two.js thinks you're running a local server and attempting to find assets from the http
protocol instead of directly from node's FileSystem
.
The getAbsoluteURL
needs a fix there, but I don't believe for the issue you're running into. I say this, because this example works fine for me with the latest dev
branch:
var { createCanvas, Image } = require('canvas');
var Two = require('two.js');
var fs = require('fs');
var path = require('path');
var width = 800;
var height = 600;
var canvas = createCanvas(width, height);
Two.Utils.shim(canvas, Image);
var time = Date.now();
var two = new Two({
width: width,
height: height,
domElement: canvas
});
var uri = path.resolve(__dirname, './images/thumbnail.png');
var sprite = two.makeSprite(uri, two.width / 2, two.height / 2);
two.render();
var settings = { compressionLevel: 3, filters: canvas.PNG_FILTER_NONE };
fs.writeFileSync(path.resolve(__dirname, './images/rectangle.png'), canvas.toBuffer('image/png', settings));
console.log('Finished rendering. Time took: ', Date.now() - time);
process.exit();
Thanks for getting back and providing the example. Seems like in my environment the file uri gets overridden so I can't get the example to work.
I continued reading on loading images with the canvas package and found out about the loadImage method (https://github.com/Automattic/node-canvas/issues/1581#issuecomment-629274227).
/**
* Convenience function for loading an image with a Promise interface. This
* function works in both Node.js and Web browsers; however, the `src` must be
* a string in Web browsers (it can only be a Buffer in Node.js).
* @param src URL, `data: ` URI or (Node.js only) a local file path or Buffer
* instance.
*/
export function loadImage(src: string|Buffer, options?: any): Promise<Image>
Could it also be a solution to pass a loaded Image when creating a new Texture the way:
import { createCanvas, Image, loadImage } from "canvas";
...
const canvas = createCanvas(width, height);
Two.Utils.shim(canvas, Image);
const ctx = canvas.getContext("2d");
var two = new Two({
width: width,
height: height,
domElement: canvas,
});
const img = await loadImage(imgAsDataUrl);
const texture = new Two.Texture(img);
two.update();
However, I tried it on a fork of two.js but always end up with a nodejs error as soon as I call two.update();
:
FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal.
1: 0x100080c68 node::Abort() [/usr/local/bin/node]
2: 0x100080dec node::errors::TryCatchScope::~TryCatchScope() [/usr/local/bin/node]
3: 0x1001876f0 v8::V8::ToLocalEmpty() [/usr/local/bin/node]
4: 0x10300f07a Pattern::New(Nan::FunctionCallbackInfo<v8::Value> const&) [/Users/christianecker/Dev/DV/break-protector/node_modules/canvas/build/Release/canvas.node]
5: 0x1030029c8 Nan::imp::FunctionCallbackWrapper(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/christianecker/Dev/DV/break-protector/node_modules/canvas/build/Release/canvas.node]
6: 0x1001f08f0 v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) [/usr/local/bin/node]
7: 0x1001efbe4 v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<true>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) [/usr/local/bin/node]
8: 0x1001ef914 v8::internal::Builtins::InvokeApiFunction(v8::internal::Isolate*, bool, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, v8::internal::Handle<v8::internal::HeapObject>) [/usr/local/bin/node]
9: 0x1002a8f94 v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/usr/local/bin/node]
10: 0x1002a922a v8::internal::Execution::New(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) [/usr/local/bin/node]
11: 0x1001a3074 v8::Function::NewInstanceWithSideEffectType(v8::Local<v8::Context>, int, v8::Local<v8::Value>*, v8::SideEffectType) const [/usr/local/bin/node]
12: 0x103016ae6 Context2d::CreatePattern(Nan::FunctionCallbackInfo<v8::Value> const&) [/Users/christianecker/Dev/DV/break-protector/node_modules/canvas/build/Release/canvas.node]
13: 0x1030029c8 Nan::imp::FunctionCallbackWrapper(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/christianecker/Dev/DV/break-protector/node_modules/canvas/build/Release/canvas.node]
14: 0x1001f08f0 v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) [/usr/local/bin/node]
15: 0x1001efecf v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) [/usr/local/bin/node]
16: 0x1001ef5d0 v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) [/usr/local/bin/node]
17: 0x100950a19 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit [/usr/local/bin/node]
18: 0x1008d62c4 Builtins_InterpreterEntryTrampoline [/usr/local/bin/node]
Thanks for sharing. So, I've made updates to the Two.Texture
on the latest dev branch with a more robust fix: https://github.com/jonobr1/two.js/commit/ace14453effd460c2479d5eeceba45b53912a5e5. You can now use the node-canvas
Image
class as a valid argument into textures. You can still use resolved file paths and I will track using Buffers. In your case @christianecker-vil, you should be able to use your latest example with the code I've just deployed. If that doesn't work then please let me know and we can debug further.
Hope this helps!
Thank you for this quick fix, just worked like a charm!
Not the same, but related is this new enhancement to support TypedArrays: https://github.com/jonobr1/two.js/issues/472
Hi,
I am struggling when writing unit tests in a react/jest environment for my canvas drawing methods. In contrast to the built-in browser canvas, I use
node-canvas
:When creating a new Texture by
const texture = new Two.Texture(imgAsDataUrl);
I run in the same error as described in [#468 ] (ReferenceError: require is not defined
)To overcome it, I tried overriding
Two.Utils.isHeadless = false;
, injecting an already loadedImage
into aTexture
Object and registering theTexture
manually by callingTwo.Texture.Register.img(texture)
. But the image is not rendered to the canvas (I probably miss internals of Two.js, which need to be called to make it work)Also, I tried writing the DataURL image to disk using
fs
and providingnew Two.Texture()
with the file name. But this again fails withReferenceError: require is not defined
in my environment.I guess it would be easiest if there was a way to also provide images as DataUrl in headless mode, or am I missing anything?
Thanks for your help!