Storing manipulated ImageData into imageObject. Javascript - 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")};

Related

readPixels from WebGL2RenderContext returns only black pixels

So there is a game online that uses WebGL2 and WebAssembly. My goal is to interact with the game by script. It uses pointers internally which makes it hard to read data from the game data. That's why I decided to go over the UI using the WebGL context. I'm very new to WebGL, graphics and rendering in general and have no idea what I'm actually doing.
I've found the canvas and can execute methods on it. My first step is to take screenshots of areas using WebGL that I may use to analyze parts of the UI. For that I'm using WebGLRenderingContext#readPixels. Here's a snippet reading the whole canvas and saving its' pixels as RGBA:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("webgl2");
const pixels = new Uint8Array(ctx.drawingBufferWidth * ctx.drawingBufferHeight * 4);
ctx.readPixels(0, 0, ctx.drawingBufferWidth, ctx.drawingBufferHeight, ctx.RGBA, ctx.UNSIGNED_BYTE, pixels)
// Returns only black / white
pixels.findIndex(pixel => pixel !== 0 && pixel !== 255); // -1
So in this case, there are only black pixels, all 4-tuples equal (0,0,0,255). A method to draw those pixels in a temporary canvas and download its' ImageData as png creates a black image.
What's the reason behind this and how can I fix it?
For performance reasons the WebGl's drawing buffer gets cleared after drawing. https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
Any calls to readPixels() will just return empty data.
To keep it's content you need to set the preserveDrawingBuffer flag to true wen getting the drawing context via the getContext("webgl") function.
So change this
const ctx = canvas.getContext("webgl2");
to
const ctx = canvas.getContext("webgl2", {preserveDrawingBuffer: true});

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.

Hand-create images in javascript and draw with respect to the alpha channel?

I'm currently trying to create a page with dynamically generated images, which are not shapes, drawn into a canvas to create an animation.
The first thing I tried was the following:
//create plenty of those:
var imageArray = ctx.createImageData(0,0,16,8);
//fill them with RGBA values...
//then draw them
ctx.putImageData(imageArray,x,y);
The problem is that the images are overlapping and that putImageData simply... puts the data in the context, with no respect to the alpha channel as specified in the w3c:
pixels in the canvas are replaced wholesale, with no composition, alpha blending, no shadows, etc.
So I thought, well how can I use Images and not ImageDatas?
I tried to find a way to actually put the ImageData object back into an image but it appears it can only be put in a canvas context. So, as a last resort, I tried to use the toDataURL() method of a 16x8 canvas(the size of my images) and to stick the result as src of my ~600 images.
The result was beautiful, but was eating up 100% of my CPU...(which it did not with putImageData, ~5% cpu) My guess is that for some unknown reason the image is re-loaded from the image/png data URI each time it is drawn... but that would be plain weird... no? It also seems to take a lot more RAM than my previous technique.
So, as a result, I have no idea how to achieve my goal.
How can I dynamically create alpha-channelled images in javascript and then draw them at an appreciable speed on a canvas?
Is the only real alternative using a Java applet?
Thanks for your time.
Not knowing, what you really want to accomplish:
Did you have a look at the drawImage-method of the rendering-context?
Basically, it does the composition (as specified by the globalCompositeOperation-property) for you -- and it allows you to pass in a canvas element as the source.
So could probably do something along the lines of:
var offScreenContext = document.getCSSCanvasContext( "2d", "synthImage", width, height);
var pixelBuffer = offScreenContext.createImageData( tileWidth, tileHeight );
// do your image synthesis and put the updated buffer back into the context:
offScreenContext.putImageData( pixelBuffer, 0, 0, tileOriginX, tileOriginY, tileWidth, tileHeight );
// assuming 'ctx' is the context of the canvas that actually gets drawn on screen
ctx.drawImage(
offScreenContext.canvas, // => the synthesized image
tileOriginX, tileOriginY, tileWidth, tileHeight, // => frame of offScreenContext that get's drawn
originX, originY, tileWidth, tileHeight // => frame of ctx to draw in
);
Assuming that you have an animation you want to loop over, this has the added benefit of only having to generate the frames once into some kind of sprite-map so that in subsequent iterations you'll only ever need to call ctx.drawImage() -- at the expense of an increased memory footprint of course...
Why don't you use SVG?
If you have to use canvas, maybe you could implement drawing an image on a canvas yourself?
var red = oldred*(1-alpha)+imagered*alpha
...and so on...
getCSSCanvasContext seems to be WebKit only, but you could also create an offscreen canvas like this:
var canvas = document.createElement('canvas')
canvas.setAttribute('width',300);//use whatever you like for width and height
canvas.setAttribute('height',200);
Which you can then draw to and draw onto another canvas with the drawImage method.

Get image line co-ordinates for canvas

I just started to work with canvas.
I need to simulate some image in pure canvas.
image => tool => [1, 20, 80, 45.....] => canvas => canvas render image
some picuture coordinates this picture but rendered(created) via canvas
Are there any tools that help to get image lines coordinates (to map)?
So, next, I could just use them, and get a pure canvas image.
If I understood your comment correctly, you either want to draw an image onto a canvas, or convert it to vector data and then draw that on the canvas.
Drawing an image on a canvas
This is by far the simplest solution. Converting raster images to vector data is a complicated process involving advanced algorithms, and still it's not perfect.
Rendering an image on a canvas is actually very simple:
// Get the canvas element on the page (<canvas id="canvas"> in the HTML)
var ctx = document.getElementById('canvas').getContext('2d');
// Create a new image object which will hold the image data that you want to
// render.
var img = new Image();
// Use the onload event to make the code in the function execute when the image
// has finished loading.
img.onload = function () {
// You can use all standard canvas operations, of course. In this case, the
// rotate function to rotate the image 45 degrees.
ctx.rotate(Math.PI / 4);
// Draw image at (0, 0)
ctx.drawImage(img, 0, 0);
}
// Tell the image object to load an image.
img.src = 'my_image.png';
Converting a raster image to vector data
This is a complicated process, so I won't give you the whole walkthrough. First of all, you can give up on trying to implement this yourself right now, because it requires a lot of work. However, there are applications and services that do this for you:
http://vectormagic.com/home
Works great, but you will have to pay for most of the functionality
How to convert SVG files to other image formats
A good list of applications that can do this for you
After this, you can store the vector data as SVG and use the SVG rendering that some browsers have, or a library such as SVGCanvas to render SVG onto a canvas. You can probably use that library to convert the resulting image to a list of context operations instead of converting from SVG every time.

Categories