canvas like framebuffer work on chrome but not in firefox - javascript

I try to use a canvas like framebuffer, in chrome work ok but in firefox not.
Any expert in firefox can see the code?
in
https://github.com/r3www/r3/blob/master/compiler.js
lines 445..
function r3init() {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d',{alpha:false,preserveDrawingBuffer:true});
imageData=ctx.getImageData(0,0,canvas.width, canvas.height);
buf8=new Uint8ClampedArray(memdata,0,imageData.data.length);
meminidata=imageData.data.length;// dinamic???
}
function redraw() {
imageData.data.set(buf8);ctx.putImageData(imageData,0,0);
}
r3init set the canvas and redraw copy the memory of framebuffer to canvas.
for try go to
https://rawgit.com/r3www/r3/master/index.html
and click run.
Thank's

Your problem is that you are relying on a Chrome bug, which doesn't respect your alpha: false CanvasRenderingContext2DSetting, when you put a transparent ImageData.
When you do ctx.putImageData(ImageData, x, y) to such a context, the alpha channel should be ignored by the algorithm, and always fully opaque => 255.
From specs' note:
Thus, the bitmap of such a context starts off as opaque black instead of transparent black; clearRect() always results in opaque black pixels, every fourth byte from getImageData() is always 255, the putImageData() method effectively ignores every fourth byte in its input, and so on.
So, I didn't dig exactly what you do to this data, but it seems that you assume the alpha channel is initially 0, while it actually shouldn't be.
A simple fix, is to initialize your context with the default alpha set.
Also note that preserveDrawingBuffer is a webgl setting, there is no drawing buffer in 2D context.
// alpha for everyone
ctx = canvas.getContext('2d');
Simple test for currently broken Chrome (at least v69.0.3497)
const alpha = document.createElement('canvas')
.getContext('2d');
const opaque = document.createElement('canvas')
.getContext('2d', {
alpha: false
});
// a transparent ImageData
const transp = alpha.createImageData(300, 150);
// alpha channel should be ignored
opaque.putImageData(transp,0,0);
const opaque_c = opaque
.getImageData(0,0,4,4)
.data[3];
const alpha_c = alpha
.getImageData(0,0,4,4)
.data[3]
console.log('opaque', opaque_c); // should be 255
console.log('alpha', alpha_c); // should be 0

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});

Drawing tiles on canvas has blurry gaps

Example: https://jsfiddle.net/jm1y9c0L/1/
Code:
const context = document.getElementById("canvas").getContext("2d");
const scale = document.getElementById("scale");
const translate = document.getElementById("translate");
scale.value = 17.78598548284369;
translate.value = 10.02842190048295;
function draw() {
context.fillStyle="#f00";
context.fillRect(0,0,1,1);
context.fillRect(1,0,1,1);
}
function update() {
const s = Number(scale.value);
const t = Number(translate.value);
context.clearRect(0,0,100,100);
context.save();
context.translate(t,t);
context.scale(s,s);
draw();
context.restore();
}
update();
Question: How do I draw tiles on canvas in a scaled context on canvas without blurry gaps?
A few things to note:
If I don't change the background to black, things look ok:
https://jsfiddle.net/jm1y9c0L/2/
I think if I draw all tiles on a buffer and draw the buffer on the canvas scaled, it would work. But that has many issues, one being it potentially uses a lot of memory if I'm scaling a very large buffer smaller.
Edit:
To explain my goal better, I want to draw two rectangles next to each other with its boundaries being a fraction. So if I draw two rectangles, one in green and one in red with a black background. I want to see the boundary being half green, half red, but not black at all.
It is because you are using floats and not integers, canvas cant draw 0.78598548284369 of a pixel, I would recommend putting Math.floor() around your scale in the scale() function:
context.scale(Math.floor(s),Math.floor(s));
Hope this helps :D
You can't do subpixel rendering in a browser (at least you can't expect consistent results). In your case, the end of one rectangle isn't necesarily the start of the other, since there may or may not be a pixel-wide gap which comes from rounding.
You might even notice some weird behaviour, such as getting different results in different screen positions, different browsers, when resizing, etc.
The solution is to calculate the value of each pixel yourself, not let the browser do it for you.

Canvas linear downscaling

im trying to do a downscale of an image using canvas to later use the data for a hash compare. however i noticed that the canvas (or at least the simple code i use) uses no mipmap filter resulting in very sharp result and makes the test against another existing hash fail (downscaling the image in gimp using linear works as expected). the code i use to downscale is
var canvas = document.createElement("canvas");
canvas.width = width; canvas.height = height;
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0, width, height);
return context.getImageData(0, 0, width, height).data;
this results in this image (left) to the expected (right)
how can i get the canvas to downscale linear?
The new canvas draft specify a way to set re-sampling/interpolation for the canvas. The current method is always bi-linear, or nearest-neighbor if imageSmoothingEnabled = false (both methods are for both up-scaling and down-sampling). The new property is called imageSmoothingQuality:
context . imageSmoothingQuality [ = value ]
The value can be "low", "medium" and "high" (for example something like bi-cubic/Lanczos). However, no browsers has yet implemented this at the moment of writing this and the actual algorithms used for each value is not mandated.
The alternative approaches is to manually re-sample when you need changes above 50% using multiple steps, or to implement a re-sampling algorithm.
Here is an example of multiple steps to achieve bi-cubic quality level (and avoids initial CORS problems), as well as one showing the Lanczos algorithm (need CORS requirements to be met).
In addition to that you can apply sharpening convolution to compensate for some of the lost sharpness.

KineticJS click detection inside animated shapes

OK, I admit I tried to be clever: I thought if I overrode Shape's drawFunc property I could simply draw whatever inside a rectangle and still use KineticJS's click detection. Here's my attempt:
var shape = new Kinetic.Shape({
drawFunc: function(context) {
var id = 26; // Id of a region inside composite image.
context.beginPath();
context.rect(0, 0, w, h);
context.closePath();
this.fill(context);
this.stroke(context);
context.drawImage(copyCanvas, (id % 8) * w, flr(id / 8) * h,
w, h, 0, 0, w / 2, h / 2);
},
draggable: true
});
So, the idea was to draw a rectangle, and use drawImage() to draw something on top of the rectangle (like a texture, except it changes from time to time because copyCanvas itself changes). All the meanwhile, I expected event handling (drag-n-drop, in particular) to still 'just work'. Well, here's what happens: the part of the rectangle not covered by my drawImage() correctly detects clicks. However, the one fourth of the rectangle that is covered by the image refuses to respond to clicks! Now, my question is why? I dug into the KineticJS code, and looked to me that click detection simply means drawing to a buffer and seeing if a given x, y point has non-zero alpha. I can't see how this could be affected by my drawing an image on top of my rectangle.
Any ideas what's going on?
OK, so I went ahead and looked at the source code. Here's the definitive answer:
KineticJS assigns a random and unique RGB color to each shape that's created using a global map from RGB colors to shape objects. The draw() function of the shape is called twice: once with the 'real' canvas, and once with a 'buffer' canvas used for hit detection. When using the 'buffer' canvas, KineticJS switches the stroke and fill colors to the unique RGB color of the given shape. The same 'buffer' canvas is used for all shapes on a layer. Thus hit detection simply becomes reading the RGB value of a given point and looking up the corresponding shape in the global map. Now, in my example I drew an image in a way that circumvented KineticJS's juggling of colors used for hit detection. Thus, when I clicked on the image area, KineticJS saw some unknown RGB color on the buffer canvas with no known shape assigned to it.
The solution is not to draw the image for the 'buffer' (or 'hit detection') phase: a simple rectangle will do. In case you're wondering, here's the correct code for the drawFunc:
var width = 200;
var height = 100;
var myShape = new Kinetic.Shape({
drawFunc: function(context) {
if (layer.bufferCanvas.context == context) {
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
this.fill(context);
this.stroke(context);
} else {
context.drawImage(someCanvasWithAnythingOnIt, 0, 0, width, height,
0, 0, width, height);
}
}});
Can I collect my own reward?
I think your problem lies in the order. There is a depth associated with each object that you draw and the default ordering is like a stack, last drawn is on top.
Now that you have modified the code, making 2 draws inside the shape draw function, I still think the ordering is preserved and hence, the object is not able to detect the input. Try changing the order, i.e. draw image first and then the rectangle and see if the problem is solved.
Else, share a jsFiddle for an example.

putImageData not working checked existing questions

ctx.drawImage(preview,0,0, preview.width, preview.height)
var imgdata = ctx.getImageData(0,0, preview.width, preview.height).data;
ctx.putImageData(opaque(ctx.createImageData(10,10)), 0,0)
The opaque function sets the alpha data to 1. so as to pain a black square of 10*10.
function opaque(imgData){
for(var i=0, data = imgData.data; i<data.length; i=i+4){
data[i+3] = 1;
}
return imgData;
}
But my canvas looks white.
What are you trying to do?
If you want to draw a black square, use context.fillRect(...). It's more efficient than manipulating imageData.
If you want to make parts of your imageData wholly transparent, you need to set the alpha channel to 0. The alpha channel is in range [0..255], with zero meaning wholly transparent, and 255 meaning wholly opaque.
Though I haven't actually run it, from reading your opaque function, all it seems to do is make its imageData argument's pixels almost wholly transparent.

Categories