putImageData of only portion of image data object - javascript

I have an ImageData object which holds the screenshot of all monitors, so it is one huge ImageData. I want to now draw one monitor per time. I have all the monitor dimensions.
So I'm trying to use ctx.putImageData(myImgDat, topLeftMonX, topLeftMonY, monWidth, monHeight) but its not working, I dont think I understand the dirty concept so well as seen in docs: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData#Understanding_putImageData
Is it possible to draw portion of imgaedata to canvas?

Yes, it's possible, it doesn't work on your code is because your arugments passed is not met the format of the function.
As MDN CanvasRenderingContext2D.putImageData() describes, it accepts 3 or 7 arguments, you have to pass either 3 or 7, otherwise, in your example, monWidth and monHeight will be used as dirtyX and dirtyY instead of dirtyWidth and dirtyHeighy. You code would probably do
Copy the rect(monWidth, monHeight, IMAGEDATA_WIDTH, IMAGEDATA_HEIGHT).
Put it on canvas's rect(topLeftMonX + monWidth, topLeftMonY + monHeight, IMAGEDATA_WIDTH, IMAGEDATA_HEIGHT).
So, and it's somehow not very straight to put the target region to (0,0) of the target canvas, to achieve by your give condition, you may have to do:
First move imageData's (0,0) to target canvas's (-topLeftMonX,
-topLeftMonY).
Then start to put imageData at imageData's (topLeftMonX, topLeftMonY) which now is at the position (0, 0) of canvas.
The rectangle size will be topLeftMonX x topLeftMonY.
ctx.putImageData(myImgDat,-topLeftMonX, -topLeftMonY, topLeftMonX, topLeftMonY, monWidth, monHeight);
Code above will copy the rect(topLeftMonX, topLeftMonY, monWidth, monHeight) on myImgDat to: rect(0, 0, monWidth, monHeight) on canvas.
You can take a look at how it works from the snippet below.
var canvas = document.getElementById('bigCanvas')
,ctx = canvas.getContext('2d');
var tcanvas = document.getElementById('testCanvas')
,tctx = tcanvas.getContext('2d');
var grd = tctx.createRadialGradient(150, 100, 10, 150, 110, 150);
grd.addColorStop(0, "black");
grd.addColorStop(0.15, "blue");
grd.addColorStop(0.3, "cyan");
grd.addColorStop(0.5, "green");
grd.addColorStop(0.7, "yellow");
grd.addColorStop(0.85, "orange");
grd.addColorStop(1, "red");
tctx.fillStyle = grd;
tctx.fillRect(0, 0, 300, 200);
var imageData = tctx.getImageData(0, 0, 300, 200);
// Move imagedata's origin to (-150, -100) on canvas,
// start to put data on canvas at imgae data's (150, 100) and size is 150x100
// => copy rect(150, 100, 150, 100) to canvas' s rect (0, 0, 150, 100)
ctx.putImageData(imageData, -150, -100, 150, 100, 150, 100);
// Move imagedata's origin to (150, 100) on canvas,
//start to put data on canvas at imgae data's (0, 0) and and size is 150x100
// => copy rect(0, 0, 150, 100) to canvas' s rect (150, 100, 150, 100)
ctx.putImageData(imageData, 150, 100, 0, 0, 150, 100);
// Move imagedata's origin to (150, -100) on canvas,
// start to put data on canvas at imgae data's (0, 100) and size is 150x100
// => copy rect(0, 100, 150, 100) to canvas' s rect (150, 0, 150, 100)
ctx.putImageData(imageData, 150, -100, 0, 100, 150, 100);
// Move imagedata's origin to (-150, 100) on canvas,
// start to put data on canvas at imgae data's (200, 0) and size is 100x100
// => copy rect(200, 0, 150, 100) to canvas' s rect (50, 100, 150, 100)
ctx.putImageData(imageData, -150, 100, 200, 0, 100, 100);
<div>Canvas for put ImageData:</div>
<canvas id="bigCanvas" width="300" height="200"></canvas>
<hr/>
<div>Canvas for get ImageData:</div>
<canvas id="testCanvas" width="300" height="200"></canvas>

This is another solution:
var iref = imagedata.data;
// start - because took a single screenshot of alllll put togather, lets portion out the imagedata
console.time('portion out image data');
for (var i=0; i<collMonInfos.length; i++) {
var screenUseW = collMonInfos[i].w;
var screenUseH = collMonInfos[i].h;
var screnImagedata = new ImageData(screenUseW, screenUseH);
var siref = screnImagedata.data;
var si = 0;
for (var y=collMonInfos[i].y; y<collMonInfos[i].y+screenUseH; y++) {
for (var x=collMonInfos[i].x; x<collMonInfos[i].x+screenUseW; x++) {
var pix1 = (fullWidth*y*4) + (x * 4);
var B = iref[pix1];
siref[si] = iref[pix1+2];
siref[si+1] = iref[pix1+1];
siref[si+2] = B;
siref[si+3] = 255;
si += 4;
}
}
collMonInfos[i].screenshot = screnImagedata;
}
console.timeEnd('portion out image data');

Related

Javascript adjust object coordinates after canvas crop

I need to crop my current canvas background using coordinates, and the canvas have some moveable object.
The movable object which allow moved by mousemove.
var object = {
x: 250,
y: 200,
h: 16,
w:50
}
Let say i want to crop the canvas start from (100, 200) and crop size is 200*150.
So I resize the new canvas to 200*150 and draw the cropped area here.
ctx.drawImage(image,
100, 200, // Start at 100, 200 pixels from the left and the top of the image (crop),
200, 150, // "Get" a `200 * 150` (w * h) area from the source image (crop),
0, 0, // Place the result at 0, 0 in the canvas,
200, 150); // With as width / height: 200 * 150 (actual cropped area)
How to adjust my current object coordinates after canvas crop? In this case the object are in the cropped area.

fillText on canvas keeps going behind the background

I'm using ctx.globalCompositeOperation = 'destination-over'. Though the text still goes behind.
Game.setup();
ctx = Game.context;
if (AllImagesLoaded === true) {
loading = false;
clearInterval(int);
BackgroundsA.push(true);
ctx.globalCompositeOperation = 'destination-over';
Interact = new text(window.innerWidth / 2, window.innerHeight / 2 - 200, 'Press F to Interact', 'white', '30px Verdana');
Sprites.push(new sprite(window.innerWidth / 2 - 275, window.innerHeight / 2 - 315, 'idle_jack/frame_1.PNG', 600, 600, 5));
Player = new component(window.innerWidth / 2, window.innerHeight / 2 - 100, 'rgba(0, 0, 0, 0)', 50, 150);
ctx.globalCompositeOperation = 'source-over';
Backgrounds.push(new background(0, 0, 'Backgrounds/Test/test2.jpg'));
ctx.globalCompositeOperation = 'destination-over';
The "new [KEYWORDS]" are functions for filling or drawing text/components/images.
Please help!
Why don't you draw everything from behind, i.e. firstly draw the backgrounds of the game, then text and at the end player and all of the game objects ?
However if you do want to draw everything like you're trying to now, then try changing global composite operation just before drawing, it's not clear if new Background() will actually draw the background or not from your code. Here is an article to read about global composite operation:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

JavaScript context translate doesnt work in electron

I am using ctx.translate(x, y) to move Camera in canvas game. But for some reason, that doesn't work.
This is what I am using:
setCameraPos: function(x, y) {
//ctx.save()
ctx.translate(x, y)
ctx.setTransform(1, 0, 0, 1, 0, 0)
//ctx.restore()
}
It doesn't work at all. It does not change position of camera.
Any errors? No errors at all.
I am using Electron 3.0.3 Beta.
I accept any libraries.
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d')
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 30, 30)
// This doesn't work | VVV
ctx.translate(20, 20)
ctx.setTransform(1, 0, 0, 1, 0, 0)
#main {
background-color: black;
}
<canvas id="main">
</canvas>
From what you gave, the translate operation won't work anywhere, not just in Electron.
ctx.setTransform() method sets the transformation matrix to absolute values, the current matrix is discarded and the passed values are the ones to which your matrix will get set.
1, 0, 0, 1, 0, 0 are the values of the native matrix transform (i.e untransformed).
So calling ctx.setTransform(1, 0, 0, 1, 0, 0) will reset your tranform matrix to its default and make all calls to relative translate(), rotate() or transform() useless.
These methods are meant to be relative because they add up to the current matrix values. For instance,
ctx.translate(10, 10);
// here next drawing will be offset by 10px in both x and y direction
ctx.translate(40, -10);
// this adds up to the current 10, 10, so we are now offset by 30, 0
If you want your translate to work, don't call setTransform here, or even replace it with setTransform(1, 0, 0, 1, 20, 20)
Also, in your snippet, you are setting the transformation matrix after you did draw. The transformations will get applied only on next drawings, not on previous ones.
Now, you might be in an animation loop, and need your matrix to get reset at every loop.
In this case, call ctx.setTransform(1,0,0,1,0,0) either at the beginning of your drawing loop, either as the last op, and call translate() before drawing.
const canvas = document.getElementById('main');
const ctx = canvas.getContext('2d');
let x = 0;
ctx.fillStyle = 'red'
anim();
function draw() {
// reset the matrix so we can clear everything
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
//set the transform before drawing
ctx.translate(x - 30, 20)
//which is actually the same as
//ctx.setTransform(1, 0, 0, 1, x, 20);
ctx.fillRect(0, 0, 30, 30);
}
function anim() {
x = (x + 2) % (canvas.width + 60);
draw();
requestAnimationFrame(anim);
}
#main {
background-color: black;
}
<canvas id="main"></canvas>

Drawing an arc on an image inside a canvas

I am trying to clip and display a very large image inside a canvas div.
Using basic calculations and drawImage I managed to clip the image around the pixel I want and display the clipped image.
An example is here on JSFiddle (displaying image arround eye of the person)
I would like to add an arc which will be over the image around the pixel (the sx, sy pixel I use in the example in drawImage), how should I adjust the coordinates ?
var canvas = document.getElementById('test-canvas');
canvas.width = 500;
canvas.height = 285;
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function () {
//context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
context.drawImage(imageObj, 1324 - 250, 1228 - 142.5, 500, 285, 0, 0, 500, 285);
};
imageObj.src = "http://upload.wikimedia.org/wikipedia/en/b/b3/Edvard_Munch_-_Self-Portrait_-_Google_Art_Project.jpg";
An arc is part of a path, which can be either filled or stroke. In order to get your desired result, you need to move to a point on your circle*, create the arc, and then use stroke() (fiddle):
function strokeCircle(ctx, midx, midy, radius){
ctx.moveTo(midx + radius, midy);
ctx.arc(midx, midy, radius, 0, 2 * Math.PI, false);
ctx.stroke();
}
imageObj.onload = function () {
context.drawImage(imageObj, 1324 - 250, 1228 - 142.5, 500, 285, 0, 0, 500, 285);
strokeCircle(context, 250, 142.5, 30);
};
* The correct coordinate depends on your polar coordinates used for the circle. If you draw from 0 to Math.PI, you need to start on the right-most point.

How to flip images horizontally with HTML5

In IE, I can use:
<img src="http://example.com/image.png" style="filter:FlipH">
to implement an image flip horizontally.
Is there any way to flip horizontally in HTML5? (maybe by using canvas?)
thanks all :)
canvas = document.createElement('canvas');
canvasContext = canvas.getContext('2d');
canvasContext.translate(width, 0);
canvasContext.scale(-1, 1);
canvasContext.drawImage(image, 0, 0);
Here's a snippet from a sprite object being used for testing and it produces the results you seem to expect.
Here's another site with more details. http://andrew.hedges.name/widgets/dev/
You don't need HTML5, it can be done with CSS same as in IE:
-moz-transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
-o-transform: scale(-1, 1);
transform: scale(-1, 1);
filter: FlipH;
I like Eschers function above. I have made it a little neater and better. I have added flop (vertically) besides flip. Also a possibility to draw/rotate around the center of the image instead of top left. Finally, the function does not require all arguments. img, x and y are required but the rest are not.
If you were using something like context.drawImage(...), you can now just use drawImage(...) and add the rotate/flip/flop functionality explained here:
function drawImage(img, x, y, width, height, deg, flip, flop, center) {
context.save();
if(typeof width === "undefined") width = img.width;
if(typeof height === "undefined") height = img.height;
if(typeof center === "undefined") center = false;
// Set rotation point to center of image, instead of top/left
if(center) {
x -= width/2;
y -= height/2;
}
// Set the origin to the center of the image
context.translate(x + width/2, y + height/2);
// Rotate the canvas around the origin
var rad = 2 * Math.PI - deg * Math.PI / 180;
context.rotate(rad);
// Flip/flop the canvas
if(flip) flipScale = -1; else flipScale = 1;
if(flop) flopScale = -1; else flopScale = 1;
context.scale(flipScale, flopScale);
// Draw the image
context.drawImage(img, -width/2, -height/2, width, height);
context.restore();
}
Examples:
var myCanvas = document.getElementById("myCanvas");
var context = myCanvas.getContext("2d"); // i use context instead of ctx
var img = document.getElementById("myImage"); // your img reference here!
drawImage(img, 100, 100); // just draw it
drawImage(img, 100, 100, 200, 50); // draw it with width/height specified
drawImage(img, 100, 100, 200, 50, 45); // draw it at 45 degrees
drawImage(img, 100, 100, 200, 50, 0, true); // draw it flipped
drawImage(img, 100, 100, 200, 50, 0, false, true); // draw it flopped
drawImage(img, 100, 100, 200, 50, 0, true, true); // draw it flipflopped
drawImage(img, 100, 100, 200, 50, 45, true, true, true); // draw it flipflopped and 45 degrees rotated around the center of the image :-)
Mirror an image or rendering using the canvas.
Note. This can be done via CSS as well.
Mirroring
Here is a simple utility function that will mirror an image horizontally, vertically or both.
function mirrorImage(ctx, image, x = 0, y = 0, horizontal = false, vertical = false){
ctx.save(); // save the current canvas state
ctx.setTransform(
horizontal ? -1 : 1, 0, // set the direction of x axis
0, vertical ? -1 : 1, // set the direction of y axis
x + (horizontal ? image.width : 0), // set the x origin
y + (vertical ? image.height : 0) // set the y origin
);
ctx.drawImage(image,0,0);
ctx.restore(); // restore the state as it was when this function was called
}
Usage
mirrorImage(ctx, image, 0, 0, true, false); // horizontal mirror
mirrorImage(ctx, image, 0, 0, false, true); // vertical mirror
mirrorImage(ctx, image, 0, 0, true, true); // horizontal and vertical mirror
Drawable image.
Many times you will want to draw on images. I like to call them drawable images. To make an image drawable you convert it to a canvas
To convert an image to canvas.
function makeImageDrawable(image){
if(image.complete){ // ensure the image has loaded
var dImage = document.createElement("canvas"); // create a drawable image
dImage.width = image.naturalWidth; // set the resolution
dImage.height = image.naturalHeight;
dImage.style.width = image.style.width; // set the display size
dImage.style.height = image.style.height;
dImage.ctx = dImage.getContext("2d"); // get drawing API
// and add to image
// for possible later use
dImage.ctx.drawImage(image,0,0);
return dImage;
}
throw new ReferenceError("Image is not complete.");
}
Putting it all together
var dImage = makeImageDrawable(image); // convert DOM img to canvas
mirrorImage(dImage.ctx, dImage, 0, 0, false, true); // vertical flip
image.replaceWith(dImage); // replace the DOM image with the flipped image
More mirrors
If you wish to be able to mirror along an arbitrary line see the answer Mirror along line
One option is to horizontally flip the pixels of images stored in ImageData objects directly, e.g.
function flip_image (canvas) {
var context = canvas.getContext ('2d') ;
var imageData = context.getImageData (0, 0, canvas.width, canvas.height) ;
var imageFlip = new ImageData (canvas.width, canvas.height) ;
var Npel = imageData.data.length / 4 ;
for ( var kPel = 0 ; kPel < Npel ; kPel++ ) {
var kFlip = flip_index (kPel, canvas.width, canvas.height) ;
var offset = 4 * kPel ;
var offsetFlip = 4 * kFlip ;
imageFlip.data[offsetFlip + 0] = imageData.data[offset + 0] ;
imageFlip.data[offsetFlip + 1] = imageData.data[offset + 1] ;
imageFlip.data[offsetFlip + 2] = imageData.data[offset + 2] ;
imageFlip.data[offsetFlip + 3] = imageData.data[offset + 3] ;
}
var canvasFlip = document.createElement('canvas') ;
canvasFlip.setAttribute('width', width) ;
canvasFlip.setAttribute('height', height) ;
canvasFlip.getContext('2d').putImageData(imageFlip, 0, 0) ;
return canvasFlip ;
}
function flip_index (kPel, width, height) {
var i = Math.floor (kPel / width) ;
var j = kPel % width ;
var jFlip = width - j - 1 ;
var kFlip = i * width + jFlip ;
return kFlip ;
}
For anyone stumbling upon this.
If you want to do more complex drawing, the other scale-based answers don't all work. By 'complex' i mean situations where things are more dynamic, like for games.
The problem being that the location is also flipped. So if you want to draw a small image in the top left corner of the canvas and then flip it horizontally, it will relocate to the top right.
The fix is to translate to the center of where you want to draw the image, then scale, then translate back. Like so:
if (flipped) {
ctx.translate(x + width/2, y + width/2);
ctx.scale(-1, 1);
ctx.translate(-(x + width/2), -(y + width/2));
}
ctx.drawImage(img, x, y, width, height);
Here x and y are the location you want to draw the image, and width and height are the width and height you want to draw the image.
I came across this page, and no-one had quite written a function to do what I wanted, so here's mine. It draws scaled, rotated, and flipped images (I used this for rending DOM elements to canvas that have these such transforms applied).
var myCanvas = document.getElementById("myCanvas");
var ctx = myCanvas.getContext("2d");
var img = document.getElementById("myimage.jpg"); //or whatever
var deg = 13; //13 degrees rotation, for example
var flip = "true";
function drawImage(img, x, y, width, height, deg, flip){
//save current context before applying transformations
ctx.save();
//convert degrees to radians
if(flip == "true"){
var rad = deg * Math.PI / 180;
}else{
var rad = 2*Math.PI - deg * Math.PI / 180;
}
//set the origin to the center of the image
ctx.translate(x + width/2, y + height/2);
//rotate the canvas around the origin
ctx.rotate(rad);
if(flip == "true"){
//flip the canvas
ctx.scale(-1,1);
}
//draw the image
ctx.drawImage(img, -width/2, -height/2, width, height);
//restore the canvas
ctx.restore();
}

Categories

Resources