Create 2d context *without* canvas - javascript

I am currently looking for a way to create a canvas 2d rendering context without actually having a canvas element on the page. I could dynamically create a canvas element and hide it, but then again I don't want to show the image directly to the user anytime, so there's no point of actually having a canvas element in the page. So I'm basicly looking for something that is similar to
var image = new Image( );
but only for canvas 2d rendering context (pseudo code)
var context = new 2dContext( );
Is there functionality like this? I wasn't able to find anything like it. Calling
var context = new CanvasRenderingContext2D( );
which is the name of the rendering context interface by HTML5 spec just gives me awkward errors in Firefox:
uncaught exception: [Exception... "Cannot convert WrappedNative to function" nsresult: "0x8057000d (NS_ERROR_XPC_CANT_CONVERT_WN_TO_FUN)" location: "JS frame :: http://localhost/ :: <TOP_LEVEL> :: line 25" data: no]

It is possible to use a canvas without displaying it on the page. You could do the following:
// Create a canvas element
var canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
// Get the drawing context
var ctx = canvas.getContext('2d');
// Then you can do stuff, e.g.:
ctx.fillStyle = '#f00';
ctx.fillRect(20,10,80,50);
Once you've used the canvas, you can of course add it to the document
var element = document.getElementById('canvas_container');
element.appendChild(canvas);
Or you could make an image from it:
var new_image_url = canvas.toDataURL();
var img = document.createElement('img');
img.src = new_image_url;
Or you could access the canvas data as values with:
var image_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
var rgba_byte_array = image_data.data;
rgba_byte_array[0]; // red value for first pixel (top left) in the canvas

Interestingly enough, if you create a canvas object and store its context in a variable, that variable has its own pointer to the canvas object. Since you can't use getContext("2d") without a canvas, you might as well only have one canvas pointer. If you're like me and hate having a two references to the same object, you could do this:
Original:
var canvas=document.createElement("canvas");
var context=canvas.getContext("2d");
alert(Boolean(context.canvas==canvas));// true.
What I'm talking about:
var context=document.createElement("canvas").getContext("2d");
alert(context.canvas);// The canvas object.
Now you can do all of your important canvas stuff through the context variable. After all, context is accessed more often than the canvas variable. When you do need it just reference it through the context:
context.canvas.width=320;
context.canvas.height=320;
document.body.appendChild(context.canvas);
And if you don't want to bother with the canvas just leave the variable alone, it's not like you wanted to use it anyway.

How about: OffscreenCanvas()?
And the answer is probably no, since this is only supported in Firefox 44.0+ currently.
var offscreen = new OffscreenCanvas(256, 256);
https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
https://html.spec.whatwg.org/multipage/scripting.html#the-offscreencanvas-interface
(Added for reference here, as this may well be new to the spec since this question was asked more than 6 years ago! And hopefully will be more widely adopted)

Related

Scope of document elements created in javascript

I'm new to javascript and web development in general, and I'm trying to write a renderer that can draw each slice in a CT scan as a 2D image.
I have a long thin (512x49664) image made from 512x97 slices, each of which is just a 512x512 image. I've already ascertained that this will upset webgl, so I was planning to grab individual slices from the image by drawing it on a canvas and copying the image data into a texture.
My question is: if I have a function in which I do something like:
// Create a small canvas to contain a single slice.
function getSlice(sliceNumber){
var sliceCanvas = document.createElement("canvas");
sliceCanvas.width = 512;
sliceCanvas.height = 512;
var sliceContext = sliceCanvas.getContext('2d');
sliceContext.drawImage(image, 0, 512*sliceNumber, 512, 512, 0, 0, 512, 512);
}
What happens to the canvas I created when the function exits?
It hasn't been:
Added to the DOM
Stored in a variable or property that is still in scope
Returned anywhere
… there are no references remaining to it, so it will be marked for garbage collection.
When you create a DOM element via javascript DOM API, you should attach this element to the page document.
Otherwise this element will never shown in your page.
So you have to add a line like this in your code:
document.body.appendChild(sliceCanvas);
If you call your function multiple times, you should check the canvas creation:
var sliceCanvas = document.getElementsByTagName('canvas')[0];
or
var sliceCanvas = document.getElementById('myCanvasId');
Then check:
if (!sliceCanvas) {
sliceCanvas = document.createElement('canvas');
sliceCanvas.id = 'myCanvasId'; // optional
document.body.appendChild(sliceCanvas);
}
// here your code...
UPDATE:
Consider to change the document.body with the proper DOM element where you want to place your canvas.

Resizing images using createjs / easeljs

I'd like to dynamically downsize some images on my canvas using createjs, and then store the smaller images to be displayed when zooming out of the canvas for performance reasons. Right now, I'm using the following code:
var bitmap = createjs.Bitmap('somefile.png');
// wait for bitmap to load (using preload.js etc.)
var oc = document.createElement('canvas');
var octx = oc.getContext('2d');
oc.width = bitmap.image.width*0.5;
oc.height = bitmap.image.height*0.5;
octx.drawImage(bitmap.image, 0, 0, oc.width, oc.height);
var dataUrl = oc.toDataURL('image/png'); // very expensive
var smallBitmap = new createjs.Bitmap(dataUrl);
This works, but:
The toDataURL operation is very expensive when converting to image/png and too slow to use in practice (and I can't convert to the faster image/jpeg due to the insufficient quality of the output for all settings I tried)
Surely there must be a way to downsize the image without having to resort to separate canvas code, and then do a conversion manually to draw onto the createjs Bitmap object??
I've also tried:
octx.drawImage(bitmap.image, 0, 0, oc.width, oc.height);
var smallBitmap = new createjs.Bitmap(oc);
But although very fast, this doesn't seem to actually work (and in any case I'm having to create a separate canvas element every time to facilitate this.)
I'm wondering if there is a way that I can use drawImage to draw a downsampled version of the bitmap into a createjs Bitmap instance directly without having to go via a separate canvas object or do a conversion to string?
If I understand correctly, internally this is how the createjs cache property works (i.e. uses drawImage internally to write into the DisplayObject) but I'm unable to figure out how use it myself.
You have tagged this post with createjs and easeljs, but your examples show plain Canvas context usage for scaling.
You can use the scale parameter on Bitmap.cache() to get the result you want, then reuse the cacheCanvas as necessary.
// This will create a half-size cache (50%)
// But scale it back up for you when it displays on the stage
var bmp = new createjs.Bitmap(img);
bmp.cache(0, 0, img.width, img.height, 0.5);
// Pull out the generated cache and use it in a new Bitmap
// This will display at the new scaled size.
var bmp2 = new createjs.Bitmap(bmp.cacheCanvas);
// Un-cache the first one to reset it if you want
bmp.uncache();
Here is a fiddle to see it in action: http://jsfiddle.net/lannymcnie/ofdsyn7g/
Note that caching just uses another canvas with a drawImage to scale it down. I definitely would stay away from toDataURL, as it not performant at all.

Dynamically adding new fabric canvases

I am working on a project that requires creating multiple canvas elements (separate bars each representing one distinct gradient). I would like to do this dynamically with fabric.js, eg:
function add_gradient(color_stops){
// Add a new rectangular canvas with fill representing specified gradient
var grad_box = document.getElementById("divWithCanvases");
var newCanvas = document.createElement("canvas");
grad_box.appendChild(newCanvas);
var gradCanvas = fabric.Canvas(newCanvas, {width: 500, height:50});
var ctx = gradCanvas.getContext('2d');
// Do stuff to canvas...
}
However, the call to fabric.Canvas fails with an error "this.initialize is undefined". (fabric.js line 1627, version 1.4.13)
How can I either:
Generate a new fabric canvas based on an HTML element (instead of the string id), or,
Append an auto-generated new canvas element to the DOM? (the form fabric.Canvas() without arguments will make... something... but it can't be used with appendChild)
According to the fabric.js documentation, the fabric.Canvas constructor accepts either an HTMLElement, or a string element id. I can only make it work with the string.
The function fabric.Canvas is a constructor, you have to call it with the new operator:
var gradCanvas = new fabric.Canvas(newCanvas, {width: 500, height:50});
I was getting the above error because of <div id="canvas"> </div>
Make sure your html element should be canvas
<canvas id="canvas"> </canvas>
When we want to add canvas dynamically and want to call canvas fabric API then we need to give a different name like this..
var html_canvas = document.createElement('canvas');
html_canvas.id = "CursorLayer";
var canvas = new fabric.Canvas(html_canvas, canvasConfigOptions);
// canvasConfigOptions is optional
Now we can perform all fabric operations on this canvas

Storing manipulated ImageData into imageObject. Javascript

I want to load an image, copy it e.g. 2 times, and manipulate the other 2 images. Somehow I want to do some "post-processing" stuff with the two clones. I DON'T want to get canvas parts and manipulate them. I am programming a realtime 2D game and it would be madness to manipulate every image within every frame.
Many of the solutions I found were just "cutting" parts from the canvas, dealing with them and writing them back onto the canvas. But I want to have the manipulated images stored in imageObjects to be able to directly draw them as if they were the "real" image.
A canvas is still the best way of manipulating images. Canvas slices that you get via context.getImageData() can be painted back via context.putImageData() (see Pixel manipulation with canvas) so I don't see a real advantage of converting image data into images. However, if you prefer a "real" image object, you can use canvas.toDataURL() and create an image object with this URL. Something along these lines:
// Create temporary canvas
var canvas = document.createElement("canvas");
canvas.setAttribute("width", imageData.width);
canvas.setAttribute("height", imageData.height);
// Put image data into canvas
var context = canvas.getContext("2d");
context.putImageData(imageData, 0, 0);
// Extract canvas data into an image object
var image = new Image();
image.src = canvas.toDataURL("image/png");
image.onload = function() {alert("Image object can be used")};

context.drawImage(bufferContext,0,0) returning Type error?

I have been playing about with canvas, but have stumbled across a problem. When reading up on canvas I found a few different ways to declare the canvas and it's context, both seemed to work until I tried implementing a simple frame buffer.
function drawToCanvas(){
var buffer = $("#myBuffer");
var canvas = $("#myCanvas");
var bufferContext = buffer.get(0).getContext("2d");
var context = canvas.get(0).getContext("2d");
//var buffer = document.getElementById("myBuffer");
//var canvas = document.getElementById("myCanvas");
//var bufferContext = buffer.getContext("2d");
//var context = canvas.getContext("2d");
bufferContext.fillRect(100,100,100,100);
context.drawImage(buffer, 0, 0);
}
If I use the commented out lines to draw the buffer image to the context it works perfectly. However if I use the above lines the drawImage() function gives a typeError. This was confusing me considering I was able to use the JQuery selector method fine, it was just the drawImage() function failing me.
Any advice would be really appreciated.
When using the jquery selectors, the buffer parameter used in drawImage is the jquery object, i guess. You should probably change it to buffer.get(0) ? I've never used jquery myself, but that's what I infer from the rest of your code.

Categories