Pixel manipulation with FabricJS - javascript

I'm using FabricJS v1.5.0 and have a problem when manipulated pixel by pixel on the canvas.
this.canvas = new this.fabric.Canvas('canvas');
let imageData = this.canvas.contextContainer.getImageData(0, 0, this.canvas.getWidth(), this.canvas.getHeight());
//here a changed imageData
this.canvas.contextContainer.putImageData(imageData, 0, 0);
//data seted in canvas.
let imageAdd = new this.fabric.Image(image, {
left: 100,
top: 100,
width: 100,
height: 100
});
this.canvas.add(imageAdd).setActiveObject(imageAdd);
//in this moment, the canvas don't have more the imageData setted
Why add image clear imageData? I need to transform imageData in FabricJS Object to add in canvas?

Yes, I think you do need to turn your image data into a fabricjs image. When you need to save the state of the canvas you can do this:
var dataUrl;
....
// Save canvas state
dataUrl = canvas.toDataURL()
// Make changes to the canvas
Then when you need to restore the canvas from it's saved state:
var img = fabric.Image.fromURL(dataUrl, function (i) {
canvas.clear();
canvas.add(i);
canvas.renderAll();
});
You should then be able to add other images as before.

What you need to find out is if your image is actually an image object, because fabric.Image() needs your image object.
If it's a URL or Blob format of your image ( which I guess it is ), you need to use fabric.Image.fromURL() and provide the src of the image not the whole image object.
Here is an example on how to create an instance of fabric.Image from a URL string
fabric.Image.fromURL(link, function(img) {
img.set({
id : 'someId',
width : canvas.width / 2,
height : canvas.height / 2
});
canvas.add(img).renderAll().setActiveObject(img);
});

Related

Set background image for Fabric Canvas dynamically

I want to add draw shapes on a Image, so decided to use Fabric JS. I am able to get all shapes and objects working well. Now How do I background set exactly to Image. The Image is dynamic, I want to draw on top of that image, and basically store the annotations of objects for backend processing.
Existing solution using Canvas
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = 903;
canvas.height = 657;
var background = new Image();
background.src = "http://www.samskirrow.com/background.png";
// Make sure the image is loaded first otherwise nothing will draw.
background.onload = function(){
ctx.drawImage(background,0,0);
}
Reference - https://stackoverflow.com/a/14013068/2094670
I like this solution.
Live Demo
My Current Fabric JS Code
// Initialize a simple canvas
var canvas = new fabric.Canvas("c", {
hoverCursor: 'pointer',
selection: true,
selectionBorderColor: 'green',
backgroundColor: null
});
// Define the URL where your background image is located
var imageUrl = "../dog.jpg";
// Define
canvas.setBackgroundImage(imageUrl, canvas.renderAll.bind(canvas), {
// Optionally add an opacity lvl to the image
backgroundImageOpacity: 0.5,
// should the image be resized to fit the container?
backgroundImageStretch: false
});
Html
<canvas id="c"></canvas>
Problem.
The image of dog is full hd image, however I see only portion of it. How to do render the background image to its actual height and width using Fabric Canvas ? Since Images are comming dynamically, I might not know exact height and width to hardcode it.
Here's the way to set canvas size base on image size via fabric.js.
It's done initiating the Fabric canvas inside the onload callback, that way we are able to get the image width and height, then set them into the setDimesions method.
img.onload = function () {
...
canvas.setDimensions({ width: img.width, height: img.height });
};
var imageUrl = "http://i.imgur.com/yf6d9SX.jpg";
var background = new Image();
background.src = imageUrl;
// wait for the image to load, then set Fabric Canvas on the callback that runs after image finishes loading
background.onload = function () {
var canvas = new fabric.Canvas("c", {
hoverCursor: "pointer",
selection: true,
selectionBorderColor: "green",
backgroundColor: null,
});
// set canvas width and height based on image size
canvas.setDimensions({ width: background.width, height: background.height });
canvas.setBackgroundImage(imageUrl, canvas.renderAll.bind(canvas), {
// Optionally add an opacity lvl to the image
backgroundImageOpacity: 0.5,
// should the image be resized to fit the container?
backgroundImageStretch: false,
});
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.1.0/fabric.min.js"></script>
<canvas id="c"></canvas>

How to convert dataurl back to image and display on canvas

I have 3 canvases. I crop a region from canvas1 and display it on canvas 2 . Then I want to convert canvas 2 image to a URL and to see if can convert that URL back to a image. I want it to be displayed in canvas c4.Any help is appreciated.
// image is drawn here , I want this image to be converted to a dataURL
//then back to image and display it in canvas c4
var c2 = document.getElementById("area_c2");
var ctx = c.getContext("2d");
var ctx2 = c2.getContext("2d");
image_src.width = c2.width;
image_src.height = c2.height;
ctx2.drawImage(image_src, 0, 0,image_src.width, image_src.height);
var c4 = document.getElementById("area_c4");
var ctx4 = c4.getContext("2d");
var dataURL = c2.toDataURL();
var myImg = new Image;
myImg.src = dataURL;
myImg.width = c4.width;
myImg.height = c4.height;
ctx4.drawImage(myImg, 0, 0, image_src.width, image_src.height); //Image //is not displayed here , I want the image to take the size of the canvas
<canvas id ="area_c2" style="width:300px;height:300px;border:3px solid
black;z-index:1" >
</canvas>
<canvas id ="area_c4" style="width:300px;height:300px;border:3px solid
black;z-index:1;background:red">
</canvas>
The reason your solution did not work is because you did not wait for the onload event. Before the onload event the image will not render.
To convert a canvas to a data URL is simple, use the .toDataURL(); method of the canvas element.
Then to convert the data URL back to an canvas image, you would first have to create an Image element and set its source as the dataURL. After getting the image onload event you can draw the image onto the canvas. This could be accomplished as shown:
Assuming the data URL is in a variable called dataURL, the canvas context is in a variable called ctx.
var img = new Image;
img.onload = () => { ctx.drawImage(img, 0, 0); };
img.src = dataURL;
The final solution would go like this:
var cdata2 = c2.toDataURL();
var cimg2 = new Image;
cimg2.onload = () => { ctx4.drawImage(cimg2, 0, 0, c4.width, c4.height); };
PS: You don't have to scale the image object by specifying the width and height, the canvas will do that when you specify the destination width and height.

not able to draw rect on existing <canvas> which rendered PDF using pdfjs

Task
Have a canvas in your page
<canvas id="pdfCanvas">
create a fabric canvas for existing canvas
new fabric.Canvas("pdfCanvas");
have mouse.down, mouse.up, mouse.move methods to enable drawing rectangles
render pdf in the above canvas "pdfCanvas" using PDF.js
browser now shows the rendered PDF
now draw rectangles on the pdf, it hides the rendered canvas but it does draw objects
Problem
Here is the fiddle to see the issue:
- run the above https://jsfiddle.net/hiitskiran/wgz8qore/2/
- you can see the fabric rectangle is hiding behind the rendered pdf
- click on the pdf canvas area to see the fabric objects
What I see from your fiddle is that you don't wait the result of page.render method, which is asyncronous, then, unfortunately, if you wait the page.render, the fabric.canvas instance will clean your previously filled canvas, and we can't do nothing for this.
So I tried another approach: First of all, I wait the pdf.js page.render method, then I invoke the .toDataURL of your previously filled canvas so that I can have a copy of the old content, then I set your previously filled canvas as background of your new fabric.canvas and finish to draw your rectangle.
var url = 'http://www.africau.edu/images/default/sample.pdf';
var canvas = document.getElementById('pdfcanvas');
var context = canvas.getContext('2d');
PDFJS.disableWorker = true;
PDFJS.getDocument(url).then(function(pdf) {
pdf.getPage(1).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({
canvasContext: context,
viewport: viewport
}).then(function() {
var bg = canvas.toDataURL("image/png");
var fcanvas = new fabric.Canvas("pdfcanvas", {
selection: false
});
fcanvas.setBackgroundImage(bg,fcanvas.renderAll.bind(fcanvas));
fcanvas.setWidth(300);
fcanvas.setHeight(300);
var rect = new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#FF454F',
opacity: 0.5,
transparentCorners: true,
borderColor: "gray",
cornerColor: "gray",
cornerSize: 5
});
fcanvas.add(rect);
fcanvas.renderAll();
});
});
});
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<script src="https://seikichi.github.io/tmp/PDFJS.0.8.715/pdf.min.js"></script>
<canvas id="pdfcanvas" style="border:1px solid black"></canvas>

How to save Zoomed and Panned Canvas to dataUrl in Javascript

I build a canvas Element in which you can zoom and pan a background image and by click you can add Icons on it. It is built on fabric js.
So once I added icons and zoomed and panned, I would like to save the image to dataurl. If I just use canvas.toDataURL, I can only see the currently shown area. Does anybody have an Idea, how I can save the entire area of the background image with all elements on the canvas? I therefore need a function, which resets the zoom and the pan and adjusts the size of the canvas to the size of the background image.
I included an example of how initialized my canvas below.
// New Fabric Canvas
var canvas = new fabric.Canvas('c'); //render image to this canvas
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.setHeight(window.innerHeight);
canvas.setWidth(window.innerWidth);
canvas.renderAll();
}
// resize on init
resizeCanvas();
// Load image
var image = new Image();
image.onload = function() {
loadImage(image, canvas);
canvas.height = image.height;
canvas.width = image.width;
}
function loadImage(image, canv) {
var img = new fabric.Image(image, {
selectable: false,
lockUniScaling: true,
centeredScaling: true,
alignX: "mid",
alignY: "mid",
});
canv.add(img);
canv.centerObject(img);
}
image.src = 'http://placehold.it/350x150';
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.1.0/fabric.all.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="canvasarea" style="background: #000;">
<canvas id='c' onclick="return false;"></canvas>
</div>
If you just want to generate an image and not change the zoom level of canvas, you can pass multiplier parameter to toDataUrl function.
var dataURL = canvas.toDataURL({
format: 'png',
multiplier: 2
});
This will generate an image after zooming in 200%.
You can even specify the part of the canvas, you want to generate an image of by using:
var dataURL = canvas.toDataURL({
format: 'png',
left: 100,
top: 100,
width: 200,
height: 200
});
Check the details here, toDataUrl function:
http://fabricjs.com/docs/fabric.Canvas.html

fabric.js: Does resizing image using Image.set() reduce image quality?

I need to reduce the height/width of an image before adding to a Canvas. We are using fabric.js.
When the image is resized using the image.set() function shown below, does it degrade the image quality?
imgAdd.onload = function () {
var img = new fabric.Image(imgAdd);
var imgSizeObj = img.getOriginalSize();
var newImgSize = calculateAspectRatioFit(imgSizeObj.width, imgSizeObj.height, canvas.width, canvas.height);
if (imgSizeObj.width > canvas.width || imgSizeObj.height > canvas.height) {
//Does img.set() preserve image resolution?
img.set({
width: newImgSize.width,
height: newImgSize.height
});
}
canvas.add(img);
}
The goal is to maintain image quality but automatically scale a large image to fit within the canvas.
Thank you,
No it doesnt.
The image is stored in a property of the object._element and does not change unless you apply some filter over it.
Even in that situation a copy is stored in object._originalElement.
Please try the following:
var scale = bef;
img.set({
scaleX: scale,
scaleY: scale
});
You can find more information on this here: https://github.com/kangax/fabric.js/issues/176

Categories

Resources