How to draw on HTML5 canvas without putImageData()? - javascript

I found this issue https://code.google.com/p/android/issues/detail?id=17565 and it seems that it is not going to get solved soon, so I would like to know, how can I draw on canvas without using the putImageData()?
var imgCanvas = document.createElement("canvas");
var ctx = imgCanvas.getContext("2d");
var imageObj.src = 'img/someImage.png';
imageObj.onload = function(){
ctx.draw(imageObj,0,0);
var imageData = ctx.getImageData(0,0,30,30);
var data = imageData.data;
for(var i = 0, n = data.length; i < n; i += 4) {
data[i]+=20;//red
data[i+1]-=20;//green
data[i+2]-=20;//blue
data[i+3]=0; //alpha, но я не трогаю этого параметра
}
ctx.putImageData(imageData,0,0);
}
For instance, in normal browsers, the result of changing a pixel rgba=[100,100,100,200] with the above script should be rgba=[120,80,80,200], but on android's 4.2 default browser the result is not the expected, is something weird like rgba=[153,102,102,200]. Anyway, the point is that there is an issue and I would like to know, how can I change one image's rgba parameters without using the putImageData() method?. If there is not a way with canvas, I would like to know, how can solve this another way? I need to change the image color dinamically, and I need this to work on android (I am using phonegap and the result is the same as in the default android browser). Thank you!

If you want to change the full image color by a factor (for example, add 10 to r, 30 to g and 4 to b), you could use globalComposite operations:
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Compositing
Check 'lighter' and 'darker' ops.
If you want to set a different color / factor to each pixel, i think that putImageData is the way to go...
Hope it helps.

Related

how to access image data in Elm?

How do we get the Pixel data from images in Elm?
Here in JavaScript, is code to get the color of a set of pixels in a figure (taken from here)
var image = new Image;
image.src = "starry-night.jpg";
var canvas = d3.select("body").append("canvas");
var context = canvas.node().getContext("2d");
context.drawImage(image, 0, 0);
// beware variable name "image" got used twice
image = context.getImageData(0, 0, width, height);
var x = Math.random()*width,
y = Math.random()*height,
i = (y * width + x) << 2;
pixelColor = d3.rgb(image.data[i + 0], image.data[i + 1], image.data[i + 2]) + "";
The code loads an image to a <canvas> element, then extracts the color of a pixel from the canvas using image.getImageData().
Can we interface the image.data object in Elm? Right now I don't think it's possible...
Right now Collage types are list of forms...
HTML is also a module that can put imags in the DOM.
SVG allows for some simple global image transformations but nothing at the pixel level
Elm has the Virtual Dom. In fact of problems like this, might be addressed in virtual-dom which is lower level so we are not encouraged to do this directly.
However, Elm makes a clear distinction between Collage elements and SVG elements, with no clear interface to the getImageData() function.
Do I write my own with Elm's new interOp feature?
Does a way already exist in Elm? Or a new one has to be written?
JavaScript
The << operator is called Left Shift
As suggested by #SimonH, use a port to JS until Elm provides a first-hand way to do so (if it ever does). The same approach would apply to anything you can't yet do in Elm.
I'm just answering as an answer rather than a comment for the sake of others who come here.

Image Flickering In Canvas Game

For a university project I have been tasked with creating a Flappy Bird clone. It's being done using the HTML5 canvas.
The issue doesn't happen very often, but it seems that every 6 or so seconds, the grass will flicker. I'm not sure what's causing this, it could be a performance issue.
Here is a link so you may see the issue: http://canvas.pixcelstudios.uk
Here is the function I'm using to the draw the grass:
var drawGrass = function(cWidth, ctx, minusX)
{
var x = bg_grass.x;
var y = bg_grass.y;
var w = bg_grass.w;
var h = bg_grass.h;
var img = bg_grass.img;
if (minusX[0] >= cWidth)
{
bg_grass.x = 0;
minusX[0] = 0;
}
ctx.drawImage(img, x, y, w, h);
if (minusX[0] > 0)
{
ctx.drawImage(img, w-minusX[0], y, w, h);
}
};
Basically, I'm drawing two grass sprites, each taking up a canvas width. One starts with an X of 0 and the other starts at the end of the canvas. Both are decremented each frame, then one is completely off the screen, it's completely reset to keep it looping.
I don't think it's anything to do with my update loop which is as follows:
this.update = function()
{
clearScreen();
updateBackground();
updatePositions();
checkCollisions();
render();
requestAnimFrame(gameSpace.update);
};
I've done a little bit of reading and I've read about having a second canvas to act as a buffer. Apparently this can stop flickering and improve performance? But all of the examples I've seen show the parts being drawn into the canvas out of a loop and I can't really see how doing it within a game loop (moving parts and all) would increase performance rather than decrease it. Surely the same operations are being performed, except now you also have to draw the second canvas onto the first?
Please let me know if you need any more information (although you should be able to see the whole source from the web link).
Thanks!
Okay I found the issue! Was just a simple mistake in my drawGrass function.
Due to the ordering, there'd be just a single frame where I'd set my shorthand X variable to bg_grass.x and THEN set bg_grass.x to something else, therefore drawing the wrong value.
I've now set my shorthand variables after the first if-statement.
However, if anyone could provide any insight into the second part of the question regarding a buffer canvas, I'd still much appreciate that.

getImageData doesn't read right values & putImageData doesn't update the canvas

I am having a problem with updating the canvas. The code is hosted at http://ssarangi.github.com/nombre/. The problem is I am loading a red image and after the loading is done I convert the image to Yellow and want to re-render it to the canvas.
// Turn to Yellow
function updateImage() {
// Update the image
var ctx = canvas2d.getContext("2d");
var width = canvas2d.width;
var height = canvas2d.height;
var pixels = ctx.getImageData(0, 0, width, height);
for (var x = 0; x < width; ++x) {
for (var y = 0; y < height; ++y) {
var offset = (y * width + x) * 4;
pixels.data[offset] = 0;
pixels.data[offset+1] = 255;
pixels.data[offset+2] = 255;
pixels.data[offset+3] = 255;
}
}
ctx.putImageData(pixels, 0, 0);
}
However, the ctx doesn't update the pixels. However, doing the same thing on a simple html page did work for me. This example enables WebGL though. Could someone point me to the right direction.
Thanks.
Nothing is wrong with the code provided, except that it claims to make yellow pixels when it in fact makes teal pixels. Here it is modified and working, making yellow pixels:
http://jsfiddle.net/TCf6f/
The code on your webpage works when you set a breakpoint in the chrome debugger, specifically it turns the canvas teal as your code specifies. It does not seem to work when there isn't a breakpoint, and I imagine the size of the CanvasPixelArray is to blame. What happens with a smaller canvas?
Whats weirder, sometimes you can set a breakpoint and see the operation partially working, like here:
With earlier breakpoints lead to the entire thing being the correct teal.
If a smaller canvas works, try modifying the pixel array in 4 or 8 chunks and see what happens.

JS Canvas- Put Image Data With Alpha?

I've got an ipad webapp I'm working on where you can draw on a canvas and save it. Like a more basic paint program. I need to be able to upload and image to the background and draw on it. Right now that wouldn't be too difficult since I got the drawing functionality and it wouldn't be hard to just print the image to the background and draw on it. The problem I'm having is that it also needs to have manageable layers. This means it needs to support alpha pixels.
So what I've done is written a panel class that when paint is called it moves down through it's child panels and paints their buffered images to the temp image. Then I take that and paint it over the parent- continueing until the image is flattened to a temporary image.
This works fine- especially on a desktop. But to accomplish this I had to write the putImageData code from scratch which loops through the array of pixels and paints them taking the alpha in account. Like so-
var offset = (canvasW*4*y)+x*4;
for(var r = 0; r < newHeight; r++)
{
var lineOffset = (size.width*4 - columns)*r + offset;
for(var c = 0; c < columns; c+=4)
{
var start = (r*columns)+c;
var destStart = start+lineOffset;
var red = imageData[start];
var green = imageData[start+1];
var blue = imageData[start+2];
var alpha = imageData[start+3];
var destRed = canvasData[destStart];
var destGreen = canvasData[destStart+1];
var destBlue = canvasData[destStart+2];
var destAlpha = canvasData[destStart+3];
var opacity = alpha/255;
var destOpacity = destAlpha/255;
var invOpacity = 1-opacity;
var newRed = Math.abs(red - ((red-destRed)*invOpacity));
var newGreen = Math.abs(green - ((green-destGreen)*invOpacity));
var newBlue = Math.abs(blue - ((blue-destBlue)*invOpacity));
canvasData[start+lineOffset] = newRed;
canvasData[start+lineOffset+1] = newGreen;
canvasData[start+lineOffset+2] = newBlue;
canvasData[start+lineOffset+3] = 255;
}
}
This takes about 50 miliseconds per layer. Not very good for a desktop. Takes a whopping 1200 miliseconds for the ipad! So I tested it with the original putImageData (which doesn't support alpha) and it was still not very impressive but it's the best I got I'm thinking.
So here is my problem. I know there is an overal opacity for drawing with canvases but it needs to be able to draw some pixels completely opaque and some completely transparent. Is there an putImageData that includes opacity?
If not any recommendations on how I can accomplish this?
As #Jeffrey Sweeney mentioned, try stacking canvases on top of each other. For one of my Javascript library, CInk (search there for z-index), I did the same thing.
I had one container div, which I stuffed with many canvas DOMs to mimic the layers. All canvas DOMs are absolutely positioned and their z-index define the order of the layers. In your case you will have to apply style at specific layers to set its opacity.

What is the right way to change Raphaël path elements

I try to change Raphaël path elements essentially in the following way (please regard the code includes build up stuff for a complete example):
var n = 100,
i = 0;
var values = [n];
var panel = document.createElement("div");
var paper = null;
var path = null;
panel.id = "panel";
panel.style.top = '0px';
panel.style.left = '0px';
panel.style.width = '300px';
panel.style.height = '300px';
panel.style.background = 'black';
document.getElementsByTagName('body')[0].appendChild(panel);
paper = Raphael(panel, 0, 0);
path = paper.path('m0,0');
path.attr({ stroke: '#fff', 'stroke-width': 1 });
function test () {
i = n;
while (i--)
values[i] = Math.round(Math.random() * 3);
// perform change here!
path.attr({ path: 'm0,0l0,' + values.join('l3,') });
// just a test case!
setTimeout(test, 1);
};
test();
Unfortunately this approach leaks in memory. I've tested it in FF 4 and IE 7+ with Raphaël 1.5.2 and 2.0 beta. The only difference is that Raphaël 1.5.2 leaks much faster than 2.0 beta.
What am I doing wrong?
Update
To put this question into context: I want to implement a 'realtime' graph control with Raphaël. Therefore I use an array buffer for each series and render them when the buffer size is reached, so I need only to render a given fix length series.
The only way I saw to do this in Raphaël is a path element per series which gets an update of it's path attribute .attr({path: path.attr('path') + getSvgPath(buffer)}) if necessary, followed by an translation on the x axis depending on the buffer size .animate({translation: (bufferSize*valuesDistance*-1) + ',0'}, 500, '<', callback) - for a smooth animation of the updates - and at last a shift of the path attribute after the animation to prevent ever expanding path strings: .attr({path: shiftSvgPath(path.attr('path'))}).
The functions shiftSvgPath() and getSvgPath() just returning the appropriate svg path string. So that the result always consits of one moveTo command at the beginning and a constant number of lineTo commands, either equal to the number of displayed values or plus the buffer size.
I ran into a similar problem lately. I wouldn't call it a leak, but rather a huge memory consumption from Raphael, when drawing paths. I'm just guessing it uses some caching arrays internally that eat up a lot of memory.
My approach was to ditch Raphael and draw the svg elements with plain old javascript.

Categories

Resources