PIL and javascript getImageData - javascript

I'm trying to modify pixels with Python (PIL) and read the pixels again with javascript
In python I did this,
from PIL import Image
im = Image.open("foo.png").convert('RGBA')
pix = im.load()
print im.mode
for x in range(0, 10):
for y in range(0, 10):
pix[x, y] = (50, x, y, 100)
im.save("foo_new.png")
In javascript I read images like this,
<img src="foo_new.png" id="myImage">
<br>
<canvas id="myCanvas" width="240" height="297" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
window.onload = function() {
var img = document.getElementById("myImage");
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.drawImage(img, 0, 0);
console.log(ctx.getImageData(0, 0, 10, 10).data)
}
</script>
In chrome's console I expect to see the following sequence,
[50, 0, 0, 100]
[50, 0, 1, 100]
[50, 0, 2, 100]
...
However, I'm getting this
[51, 0, 0, 100],
[51, 0, 0, 100],
[51, 3, 0, 100],
[51, 3, 0, 100]
Totally unexpected ...
But the RGB mode works just fine, I don't really get it, anyone knows?

Related

ctx.putImageData doesn't work, the canvas remains as it was before putting image data

I have a jsx component. I use useEffect to get get data from server and then set the canvas context to imageData, that I get from my server.
Here is how I set it:
const ctx = canvasRef.current.getContext('2d'),
arr = Object.values(JSON.parse(data.boardContent))
let idata = new Uint8ClampedArray(arr)
let imageData = new ImageData(idata, 6, 6)
ctx.putImageData(imageData, 0, 0)
There is no errors in console
When I run the same code in console, it works.
All of inputs are not undefind, here is the imageData data:
data: Uint8ClampedArray(144) [100, 100, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, ...

Using globalCompositeOperation to create gradient mask

I am trying to use canvas's globalCompositeOperation='destination-in' setting to draw a series of dots that are masked by a radial gradient. My desired outcome is shown in the screenshot below:
Instead, my canvas is showing the solid gradient with none of the dots visible. Here's my JS:
var canvas = document.getElementById('canvas')
, ctx = canvas.getContext('2d');
var coordMatrix = [
[50, 100, 150, 50, 100, 150],
[50, 50, 50, 100, 100, 100]
];
var gradient = ctx.createRadialGradient(100, 100, 0, 100, 100, 100);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 200);
ctx.globalCompositeOperation = 'destination-in';
coordMatrix[0].forEach(function(xCoord, i) {
var yCoord = coordMatrix[1][i];
ctx.moveTo(xCoord, yCoord);
ctx.arc(xCoord, yCoord, 10, 0, Math.PI * 2, false);
});
And here's a fiddle:
https://jsfiddle.net/73d9jawn/2/
Am I missing something?
You forgot to call ctx.fill() after setting the coordinates for the arcs. Also, you need to call ctx.fill() after the forEach has completed all iterations, otherwise globalCompositeOperation only applies to the first circle drawn. Here is an updated fiddle.

Sorting objects on canvas

I draw two bottons using a rectangle with text over top.
As you can see I get two different results using the same loop.
The first "button" has the text hidden behind the box.
The second has the text written on top.
Why is this? How does sorting work in canvas?
<body>
<canvas id="canvas" width="320" height="512"
style="position: absolute; left: 500px; top: 50px; z-index: 1;"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
canvas.style.backgroundColor = 'rgba(0, 0, 0, 0)';
context.clearRect(0, 0, 320, 16);
gameMenu();
function gameMenu(){
var buttons = [ {x: 210, y: 420, w: 80, h: 30, s: "Messages"},
{x: 210, y: 470, w: 80, h: 30, s: "Pause"} ], i = 0, r;
while(r = buttons[i++]) {
context.rect(r.x, r.y, r.w, r.h);
context.fillStyle = "rgb(26,26,26)";
context.fill();
context.fillStyle = 'White';
context.font = "16px Tahoma";
context.fillText(r.s, r.x + 18, r.y + 22);
}
}
</script>
</body>
Here is a JS Fiddle:
https://jsfiddle.net/oa84Lsxn/1/
You must begin each new path operation (==each new .rect) with context.beginPath. Otherwise all previous .rects will be redrawn along with the current .rect.
Your issue is that all previous paths are redrawn along with the new path. This means your first rect is being redrawn along with your new second rect -- causing the first rect's text to be overwritten by the first rect.
Here's a working version your code with context.beginPath added.
var canvas=document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.style.backgroundColor = 'rgba(0, 0, 0, 0)';
context.clearRect(0, 0, 320, 16);
gameMenu();
function gameMenu(){
// x,y changed to fit demo on StackSnipped window
var buttons = [ {x: 40, y: 20, w: 80, h: 30, s: "Messages"},
{x: 40, y: 70, w: 80, h: 30, s: "Pause"} ],
i = 0, r;
while(r = buttons[i++]) {
context.beginPath();
context.rect(r.x, r.y, r.w, r.h);
context.fillStyle = "rgb(26,26,26)";
context.fill();
context.fillStyle = 'White';
context.font = "16px Tahoma";
context.fillText(r.s, r.x + 18, r.y + 22);
}
}
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>

Use canvas drawImage to separate img into two halves

I'm trying to split image in my canvas.
First I'm declaring canvas in HTML:
<canvas width="600" height="400" id="canvas_1">
Canvas tag not supported
</canvas>
Then I'm unsuccesfully spliting my image:
var canvas = document.getElementById("canvas_1");
if (canvas.getContext){
var canvas_context = canvas.getContext("2d");
var img = document.getElementById("london_eye");
canvas_context.drawImage(img, 0, 0, 230, 300, 20, 20, 80, 300);
canvas_context.drawImage(img, 30, 0, 180, 300, 200, 20, 80, 300);
}
I guess I'm missing something there..
canvas_context.drawImage(img, 0, 0, 230, 300, 20, 20, 80, 300);
canvas_context.drawImage(img, 30, 0, 180, 300, 200, 20, 80, 300);
FIDDLE
Thank you for your time and advices
Original Code
var canvas = document.getElementById("canvas_1");
if (canvas.getContext){
var canvas_context = canvas.getContext("2d");
var img = document.getElementById("london_eye");
canvas_context.drawImage(img, 0, 0, 60, 120, 0, 0, 60, 120);
canvas_context.drawImage(img, 60, 0, 60, 120, 70, 0, 60, 120);
}
The last four parameters are destX, destY, destWidth, destHeight. So this is where on the canvas you want to put these pieces, you can see second piece is at 70 so its width of first piece 60 plus gap of 10.
I put a gap of 10px to show the two pieces of your img in the snippet!
var i = new Image();
i.crossOrigin = '';
i.onload = function() {
var canvas = document.getElementById("canvas_1");
if (canvas.getContext){
var canvas_context = canvas.getContext("2d");
canvas_context.drawImage(i, 0, 0, 60, 120, 0, 0, 60, 120);
canvas_context.drawImage(i, 60, 0, 60, 120, 70, 0, 60, 120);
}
};
i.onerror = function() {
i.src = 'http://cors.io?u=' + i.src;
i.onerror = function() {
document.write('Error loading image');
}
};
i.src = 'https://i.stack.imgur.com/olfBw.png';
<canvas id="canvas_1"></canvas>

HTML5 Canvas drawImage resizing issues

When I resize an image that I pull from a specific location on a spritesheet using drawImage, it appears as if drawImage will sometimes pull outside the source x + source y / swidth + sheight boundaries.
The following jfiddle illustrates what is happening: https://jsfiddle.net/cxuxyLj2/
The pertinent code is as follows:
drawingSurface.drawImage(heroSprite, 0, 64, 32, 64, 20, 144, 32, 64);
drawingSurface.drawImage(heroSprite, 0, 64, 32, 64, 20+50, 144, 32*2, 64*2);
There are lines that show above the RESIZED sprite's head (probably pulling the shoe of the same character on the first row), but not the original sprite's head.
My question is - can it be confirmed that this is a legitimate drawImage error, or am I doing something wrong?
It's actually because of the anti-alias when rescaling your sprite.
Because you do draw in a loop, without ever clearing the context, the anti-alias artifacts becomes bigger and bigger :
var canvas = document.querySelector("canvas");
var drawingSurface = canvas.getContext("2d");
var heroSprite = new Image();
heroSprite.src = "http://gopus.xepher.net/game/gfx/herosprite7.png";
render();
function render () {
setTimeout(render, 1000);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 20, 25, 32, 64);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 20+50, 25, 32*2, 64*2);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 150.6666687774, 25.33333222227, 32,64);
}
<canvas width="400" height="400" style="border:1px solid #000"></canvas>
You could avoid it by setting the imageSmoothingEnabled flag to false but it wasn't supported in IE prior to 10 ...
var canvas = document.querySelector("canvas");
var drawingSurface = canvas.getContext("2d");
drawingSurface.mozImageSmoothingEnabled = false;
drawingSurface.webkitImageSmoothingEnabled = false;
drawingSurface.msImageSmoothingEnabled = false;
drawingSurface.imageSmoothingEnabled = false;
var heroSprite = new Image();
heroSprite.src = "http://gopus.xepher.net/game/gfx/herosprite7.png";
render();
function render() {
setTimeout(render, 16);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 20, 25, 32, 64);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 20 + 50, 25, 32 * 2, 64 * 2);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 150.6666687774, 25.33333222227, 32, 64);
}
<canvas width="400" height="400" style="border:1px solid #000"></canvas>
...or by clearing the canvas at each call, you may avoid the artifacts to grow, but there will still be natural ones...
var canvas = document.querySelector("canvas");
var drawingSurface = canvas.getContext("2d");
var heroSprite = new Image();
heroSprite.src = "http://gopus.xepher.net/game/gfx/herosprite7.png";
render();
function render() {
setTimeout(render, 16);
drawingSurface.clearRect(0,0,canvas.width, canvas.height);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 20, 25, 32, 64);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 20 + 50, 25, 32 * 2, 64 * 2);
drawingSurface.drawImage(heroSprite, 0, 32, 32, 64, 150.6666687774, 25.33333222227, 32, 64);
}
<canvas width="400" height="400" style="border:1px solid #000"></canvas>
... so a final solution would be to use a buffer canvas, to only draw your sprite at normal scale (less anti-aliasing possible), then redraw
this buffer canvas at the wanted scale, you will keep in-image's anti-aliasing artifacts, but won't eat the border's of the cropped area anymore :
var canvas = document.querySelector("canvas");
var drawingSurface = canvas.getContext("2d");
var heroSprite = new Image();
heroSprite.src = "http://gopus.xepher.net/game/gfx/herosprite7.png";
var spriteCanvas = document.createElement('canvas');
var spriteCtx = spriteCanvas.getContext('2d');
var croppedSprite = function(x, y, width, height){
spriteCanvas.width = width;
spriteCanvas.height = height;
spriteCtx.drawImage(heroSprite, x, y, width, height, 0,0, width, height);
return spriteCanvas;
}
render();
function render() {
setTimeout(render, 16);
drawingSurface.drawImage(croppedSprite(0, 64, 32, 64), 20, 25, 32, 64);
drawingSurface.drawImage(croppedSprite(0, 64, 32, 64), 20 + 50, 25, 32 * 2, 64 * 2);
drawingSurface.drawImage(croppedSprite(0, 64, 32, 64), 150.6666687774, 25.33333222227, 32, 64);
}
<canvas width="400" height="400" style="border:1px solid #000"></canvas>
Also, you should definitely add more space between your sprites.

Categories