Javascript - set context2d Image as background for canvas - javascript

I'm using the below code to set the good quality image as background for canvas. If I set the image directly as background, it was working. But the image quality isn't good. So I'm using another canvas to have a good quality image and then I need to set as background. I'm using fabricjs
var _img = new Image();
_img.src = img_base64;
var ctx = canvas.getContext("2d");
_img.onload = function(){
// set size proportional to image
canvas.height = canvas.width * (_img.height / _img.width);
// resize to 50%
var oc = document.createElement('canvas'),
octx = oc.getContext('2d');
oc.width = _img.width * 0.5;
oc.height = _img.height * 0.5;
octx.drawImage(_img, 0, 0, oc.width, oc.height);
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5, 0, 0, canvas.width, canvas.height);
// Should be an instance of fabric.image
var imgInstance = new fabric.Image(ctx, {
width: canvas.width,
height: canvas.height
});
console.log(imgInstance);
canvas.setBackgroundImage(imgInstance, canvas.renderAll.bind(canvas), {
width: canvas.width ,
height: canvas.height,
/*originX: 'left',
originY: 'top'*/
// Needed to position backgroundImage at 0/0
});
};
The error is
Argument 1 of CanvasRenderingContext2D.drawImage could not be converted to any of: HTMLImageElement, SVGImageElement, HTMLCanvasElement, HTMLVideoElement, ImageBitmap
I'm using base64 actually. but for the fiddle, I'm using direct path
Here is the Fiddle
Edit : I see the image rendering in my local, But I need to set it as background.

The only reason why an image quality may not be good is because there is an high downscale/upscale factor.
Downscaling is good with image and css elements, while is bad when done on drawImage on the canvas, since the canvas use a nearest neighbour resize.
Other than that, quality of the image is preserved.
Fabricjs try to help on this offering resize filters.
You can apply a resize filter to an image when you load it so that then you work with an image of the dimension you need and that match 1:1 your canvas pixels.
Be aware that then if you export in higher resolution you will not get back the original quality of the image.

Related

How to scale small ImageData to large HTML canvas

I am trying to put image data 100x100 to canvas 1000x1000 , but cant able to do it ,
let width=1000; //canvas width
let height=1000; //canvas height
let img_w=100; //image width
let img_h=100; //image height
let img=new Image();
img.width=img_w
img.height=img_h
img.src="./flower.jpg"
var canvas = document.getElementById('mycanvas');
var context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
let pixels,scannedimg;
img.onload=()=>{
context.drawImage(img, 0, 0,width,height );
scannedimg = context.getImageData(0, 0, img.width, img.height);
pixels=scannedimg.data
console.log(pixels)
redraw();
}
let row=4*img_w;
let col=img_h;
function redraw(){
for(let i=0;i<row;i+=4){
for(let j=0;j<col;j++){
pixels[i+j*row]=0;
pixels[i+j*row+1]=0;
pixels[i+j*row+2]=0;
//pixels[i+j*400+3]=0;
}
}
scannedimg.data=pixels;
console.log(scannedimg);
context.putImageData(scannedimg,0,0,0,0,width,height);
}
i have converted the original array into a black image array (array of zeros) , but while putting on canvas , it is still 100x100
How to scale it to 1000x1000?
i don't want to iterate through 1000x1000 and set it to zero ,
i need a computationally efficient answer
Unless you outsource the pixel calculations to a WebAssembly module a JavaScript-only approach would indeed be rather slow for a large image.
Honestly I'm not sure what you are actually doing in your code.
First your drawing an unknown-sized .jpg to a 1000x1000 canvas which - unless the .jpg is also 1000x1000 - will scale and eventually distort the source image.
let width=1000;
let height=1000;
context.drawImage(img, 0, 0, width, height);
Secondly you're obtaining the pixel data of a 100x100 region from the top-left of your 1000x1000 canvas.
let img_w=100;
let img_h=100;
img.width=img_w;
img.height=img_h;
scannedimg = context.getImageData(0, 0, img.width, img.height);
Finally in your redraw() function you're rather randomly setting some of the pixels to black and draw it back to the canvas at 1000x1000 (which doesn't work that way but I will get into it later).
Let's do it a little different. Say we have a 300x200 image. First we need to draw it to a 100x100 canvas while maintaining it's aspect ratio to get the 100x100 imagedata.
This can be done using a dynamically created off-screen <canvas> element as we don't need to see it.
Now the tricky part is the CanvasRenderingContext2D putImageData() method. I assume you were thinking that the last pair of parameters for the width & height would stretch existing pixel data to fill the region specifid by (x, y, width, height). Well that's not the case. Instead we need to - again - paint the 100x100 pixel data to a same-sized off-screen canvas (or for simlicity re-use the existing) and draw it to the final canvas using the drawImage() method.
Here's everything put together:
let pixelsWidth = 100;
let pixelsHeight = 100;
let finalWidth = 500;
let finalHeight = 500;
let tempCanvas = document.createElement('canvas');
let tempContext = tempCanvas.getContext('2d');
tempCanvas.width = pixelsWidth;
tempCanvas.height = pixelsHeight;
let pixelData;
let img = new Image();
img.crossOrigin = 'anonymous';
img.onload = (e) => {
let scale = e.target.naturalWidth >= e.target.naturalHeight ? pixelsWidth / e.target.naturalWidth : pixelsHeight / e.target.naturalHeight;
let tempWidth = e.target.naturalWidth * scale;
let tempHeight = e.target.naturalHeight * scale;
tempContext.drawImage(e.target, pixelsWidth / 2 - tempWidth / 2, pixelsHeight / 2 - tempHeight / 2, tempWidth, tempHeight);
pixelData = tempContext.getImageData(0, 0, pixelsWidth, pixelsHeight);
redraw();
}
img.src = 'https://picsum.photos/id/237/300/200';
function redraw() {
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
canvas.width = finalWidth;
canvas.height = finalHeight;
tempContext.putImageData(pixelData, 0, 0);
context.drawImage(tempCanvas, 0, 0, finalWidth, finalHeight);
}
canvas {
background: #cccccc;
}
<canvas id="canvas"></canvas>

How to set a default (or base) rotation on a transformer in KonvaJS?

Some context:
In my application, I have to display generated SVGs, which causes no issues.
But at some point, the back-end may (and will) return a rotated (modified) SVG, that I display in the canvas (I do know the actual rotation).
My issue:
My issue is that I cannot find a way to set the transformer base rotation.
For example, I know the image I inserted is a 90° rotated image, and I would like to have this rotation shown through the transformer when the image is selected (image has a base "canvas-rotation" of 0°, and the transformer a base "canvas-rotation" of 90°).
Summary:
To put it in another way, is it possible to offset the transformer rotation based on the attached node ?
There is no way to set "rotation offset" for Konva.Transformer. You have to use node's rotation for that. If you have an SVG image you can draw it into the offscreen canvas with rotation, and then use that canvas for Konva.Image.
var imageObj = new Image();
imageObj.onload = function() {
const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
const ctx = canvas.getContext('2d');
ctx.translate(canvas.width / 2, 0);
ctx.rotate(Math.PI / 4);
ctx.drawImage(imageObj, 0, 0, canvas.width * Math.cos(Math.PI / 4), canvas.height * Math.cos(Math.PI / 4))
var yoda = new Konva.Image({
x: 60,
y: 60,
image: canvas,
draggable: true
});
// add the shape to the layer
layer.add(yoda);
const tr = new Konva.Transformer({
node: yoda
})
layer.add(tr);
layer.draw();
};
imageObj.src = 'https://konvajs.org/assets/yoda.jpg';
https://jsbin.com/korigegupo/edit?html,js,output

Javascript: drawImage is not taking whole picture

Here's the code:
window.onload = function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = document.getElementById("scream");
ctx.drawImage(img, 0, 0, img.height, img.width, 0, 0, 100, 100);
};
Here's the picture:
I set it to get the whole image by setting img.height and img.width. But still I see that no whole picture is there, only part: bottom part is getting cut. How can I get it? Am I entering wrong value?
Refer here
Arguments of function drawImage are
img - Specifies the image, canvas, or video element to use
sx - Optional. The x coordinate where to start clipping
sy - Optional. The y coordinate where to start clipping
swidth - Optional. The width of the clipped image
sheight - Optional. The height of the clipped imag
x - The x coordinate where to place the image on the canvas
y - The y coordinate where to place the image on the canvas
width - Optional. The width of the image to use (stretch or reduce the image)
height - Optional. The height of the image to use (stretch or reduce the image)
Correct use of your function will be
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 100, 100);

Canvas ImageData don't get more than 102,000 values?

I am getting pixels values from an image using canvas. The image size is 170*170 pixels. Here is my code:
var canvas = document.createElement("canvas");
canvas.style.width = img.width;
canvas.style.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(0, 0, img.width, img.height).data;
It works well, I have values in pixelData, until I reach pixelData[102000]... I've test it with a white image, and all the values from pixelData[0] to pixelData[101999] are 255, but then it is 0 until the end...
Somebody sees why? Maybe this is about canvas width and height?
Your canvas size is not what you think it is.
You are only setting the size of the canvas element not the canvas bitmap:
canvas.style.width = img.width;
canvas.style.height = img.height;
This means your bitmap is actually 300 x 150 pixels in size, the default size, and you're just scaling that to the size of the image (since it's all white you won't be able to detect this so easily).
Since your image is 170 x 170 pixels you will only paint part of the canvas leaving the rest to default RGBA value [0,0,0,0].
In order to properly set the size of the canvas you must edit the above mentioned lines to be:
canvas.width = img.width;
canvas.height = img.height;

HTML Canvas: How to draw a flipped/mirrored image?

I'm trying to flip/mirror an image as I paint it on an HTML canvas; I found a game tutorial showing a sprite sheet per direction a character has to face, but this doesn't seem quite right to me. Especially since each frame has a different size.
What would be the best technique to reach this goal?
I tried to call the setScale(-1, 1); on my canvas with no success. Maybe that isn't meant for this.
Thanks
You can do this by transforming the context with myContext.scale(-1,1) before drawing your image, however
This is going to slow down your game. It's a better idea to have a separate, reversed sprite.
You need to set the scale of the canvas as well as inverting the width.
drawToCanvas : function(v, context, width, height){
context.save();
context.scale(-1, 1);
context.drawImage(v, 0, 0, width*-1, height);
context.restore();
}
There are probably some performance issues with this but for me that was not an issue.
If you just flip it horizontally it will get off of bounds... so use translate to adjust its position:
ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
ctx.drawImage(img, 0, 0);
For a shorter code you can remove the translate and use the image size as negative offset in the second parameter of the drawImage (x coordinate) instead:
ctx.scale(-1, 1);
ctx.drawImage(img, canvas.width * -1, 0);
If you want to restore the context later, add save/restore before and after it all:
ctx.save();
ctx.scale(-1, 1);
ctx.drawImage(img, canvas.width * -1, 0);
ctx.restore();
You don't need to redraw the entire image when creating a reflection. An original reflection simply shows the bottom part of the image. This way you are redrawing a smaller part of the image which provides better performance and also you don't need to create linear gradient to hide the lower part of the image (since you never draw it).
var img = new Image();
img.src = "//vignette2.wikia.nocookie.net/tomandjerryfan/images/9/99/Jerry_Mouse.jpg/revision/latest?cb=20110522075610";
img.onload = function() {
var thumbWidth = 250;
var REFLECTION_HEIGHT = 50;
var c = document.getElementById("output");
var ctx = c.getContext("2d");
var x = 1;
var y = 1;
//draw the original image
ctx.drawImage(img, x, y, thumbWidth, thumbWidth);
ctx.save();
//translate to a point from where we want to redraw the new image
ctx.translate(0, y + thumbWidth + REFLECTION_HEIGHT + 10);
ctx.scale(1, -1);
ctx.globalAlpha = 0.25;
//redraw only bottom part of the image
//g.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
ctx.drawImage(img, 0, img.height - REFLECTION_HEIGHT, img.width, REFLECTION_HEIGHT, x, y, thumbWidth, REFLECTION_HEIGHT);
// Revert transform and scale
ctx.restore();
};
body {
background-color: #FFF;
text-align: center;
padding-top: 10px;
}
<canvas id="output" width="500" height="500"></canvas>

Categories