Fabric JS pixel by pixel manipulation - javascript

I have overlay image with rectangle transparent hole. Beneath is scalable and draggable image. How to cut only visible part of image ?
How to determine size and position of transparent rectangle ? Is it possible to do pixel by pixel alpha channel search only on overlay image ?
Any other ideas ?
EDIT:
Solution to another quoted problem is usefull, although it works only on whole canvas not on individual items like background, overlay or added images or shapes. Is it possible to read pixel values on individual fabric elements ?
I use overlay image as external png files.

The FabricJS API does not include a method to fetch the pixel values of an image.
You will have to draw your overlay image onto an html5 canvas and use getImageData to fetch the pixel data from that canvas.
getImageData().data contains the red, green, blue & alpha information for each pixel on the canvas.
You can test each pixel's alpha value and determine the minimum & maximum boundary of the transparent rectangle.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
img.src="http://masterblocks.co.in/static/img/logo.png";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height
ctx.drawImage(img,0,0);
// clear a test rectangle
ctx.clearRect(100,100,100,75);
// get clear rect bounds
var bounds=findCutoutBounds();
// test: stroke the bounds
ctx.lineWidth=2;
ctx.strokeStyle='red';
ctx.strokeRect(bounds.x,bounds.y,bounds.width,bounds.height);
}
// Get the imageData of that canvas
function findCutoutBounds(){
var minX=1000000;
var minY=1000000;
var maxX=-1;
var maxY=-1;
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
for(var y=0;y<ch;y++){
for(var x=0;x<cw;x++){
var n=(y*cw+x)*4;
if(data[n+3]<5){
if(y<minY){minY=y;}
if(y>maxY){maxY=y;}
if(x<minX){minX=x;}
if(x>maxX){maxX=x;}
}
}}
return({x:minX,y:minY,width:maxX-minX,height:maxY-minY});
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<h4>The bounding box of the transparent rectangle is stroked in red</h4>
<canvas id="canvas" width=300 height=300></canvas>

Related

Javascript Canvas Skew Image Vertically

Is it possible to skew an image on a canvas to make it appear as a reflection of another image?
Here is an illustration:
I need to flip and skew the image to get the desired effect, but I have not been able to get it to look like that. How can this be achieved?
I have found this example:
http://jsfiddle.net/jpnrzc9y/
ctx.save();
ctx.transform(1,0,0.3,-1,0,0);
ctx.drawImage(tree1,74,-117,20,40);
ctx.restore();
They seem to set the transform based on some random values.
But I cannot make sense of it. The values seem very random to me. Im trying to create a dynamic function that allows me to determine the amount of skew and that works for all images.
You can use context.scale to flip an image vertically:
// flip the canvas vertically
context.scale(1,-1);
Here are the steps to create a skewed reflection of an image:
Move (move==translate) the 0,0 origin to the desired x,y: ctx.translate(x,y);
Draw the original image image: ctx.drawImage(img,0,0);
Move to the bottom of the original image: ctx.translate(0,img.height);
Scale to vertically flip the image: ctx.scale(1,-1);
Apply a skew: ctx.transform(1,0,skewAngle,1,0,0);
Shrink the reflected image (just for aesthetics): ctx.scale(1,0.50);
Make the reflected image 50% opacity: ctx.globalAlpha=0.50;
Draw the reflected image: ctx.drawImage(img,0,-img.height);
Clean up by setting all transforms to default: ctx.setTransform(1,0,0,1,0,0);
Here's example code and a demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/character1.png";
function start(){
// 60x110
skewedReflection(img,25,25)
}
function skewedReflection(img,x,y){
// calc the 45 degree skew angle needed by ctx.transform
var skewAngle=-Math.tan(Math.PI/4);
// move the 0,0 origin to x,y
ctx.translate(x,y);
// draw the original image image
ctx.drawImage(img,0,0);
// move to the bottom of the original image
ctx.translate(0,img.height);
// use scale to flip the image
ctx.scale(1,-1);
// apply a skew
ctx.transform(1,0,skewAngle,1,0,0);
// shrink the reflected image
ctx.scale(1,0.50);
// make the reflected image 50% opacity
ctx.globalAlpha=0.50;
// draw the reflected image
ctx.drawImage(img,0,-img.height);
// clean up by setting all transforms to default
ctx.setTransform(1,0,0,1,0,0);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>

HTML5 Canvas globalCompositeOperation lighter and source-atop at same time

Is there a way to use the globalCompositeOperation "lighter" and at the same time use source-atop? (Aka lighter but only where there is already something drawn.)
You can only set one compositing operation at a time.
Workaround: You can use a second canvas to do your "lightenAtop":
Create a second in-memory canvas.
Draw your main canvas content onto the in-memory canvas.
Set compositing to lighten on the in-memory canvas
Draw your overlaying color/image onto the in-memory canvas
Set compositing to 'source-in' on the main canvas.
Draw the in-memory canvas onto the main canvas.
Result: Combination of lighten & source-atop compositing!
Left: original red rect, Middle: blue fill, Right: lightenAtop
Example code and a Demo:
This example just lightens with a solid fill, but you could alternatively drawImage instead of fillRect
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
ctx.fillStyle='rgba(250,0,0,255)';
ctx.fillRect(50,50,100,75);
lightenAtop('rgba(0,0,255,255)');
function lightenAtop(rgba){
var compositor=document.createElement('canvas');
var cctx=compositor.getContext('2d');
compositor.width=canvas.width;
compositor.height=canvas.height;
cctx.drawImage(canvas,0,0);
cctx.globalCompositeOperation='lighter';
cctx.fillStyle=rgba;
cctx.fillRect(0,0,cw,ch);
ctx.globalCompositeOperation='source-in';
ctx.drawImage(compositor,0,0);
ctx.globalCompositeOperation='source-over';
}
body{ background-color:white; }
#canvas{border:1px solid red;}
<h4>Original red rect "lightened" with blue rect</h4>
<canvas id="canvas" width=300 height=300></canvas>

How to fill color of more than one canvas image?

It's my first time to use html5 canvas and I have no idea yet how it works.
My problem is, I have to modify the colors of the images in the canvas. It's easy if there's only one image. But, I will have more than one, in other words, overlapping images.
To further understand my question, I created an illustration. There will be 2 image files only, Image 1 and Image 2:
This is my current code (with fiddle here too):
HTML:
<canvas id="canvas1" width="600" height="600"></canvas>
JS:
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
var ctx2 = can.getContext('2d');
ctx.fillStyle = 'yellow'; // background color. box in the middle is transparent. try changing this to see the effect
ctx.fillRect(40,0,250,300); // not sure if there's other way to fill in the tranparent area. but I created a box behind the image
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
img.src = "http://s7.postimg.org/aruxhs8mz/pink.png"; //image 1
// I want to fill in the paw image too
/*ctx2.fillStyle = 'purple';
ctx2.fillRect(40,0,500,500); */
//should I declare something like this again?
var img2 = new Image();
img2.onload = function() {
ctx2.drawImage(img2, 0, 0);
}
img2.src = "http://s7.postimg.org/69smposl7/paw.png"; //image 2
//paw initially colored light blue. i would like to customize the color of this too
I should be able to fill the paw image in the middle as well and not just the main image. How to do that?
I have created a fiddle just to enlighten you with my question.
Hope someone can help me for any advice.
Thanks a bunch!
You can do your task with Compositing.
Compositing tells the canvas what to do when drawing additional new drawings (pixels) on the canvas.
In your case, 3 compositing modes are useful to learn.
Source-over Compositing
The default method of compositing is 'source-over' where new drawings are drawn over existing pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// second draw a red source rectangle
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Source-atop Compositing
'source-atop' compositing will draw new pixels only where the new pixels overlap the existing canvas pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// set compositing to 'source-atop'
// (the new red pixels will only be drawn where
// they overlap the existing blue pixels)
ctx.globalCompositeOperation='source-atop';
// second draw a red source rectangle
// (red will overwrite only where it overlapped the blue)
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Destination-over Compositing
'destination-over' compositing will draw new pixels under the existing canvas pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// set compositing to 'source-atop'
// (the new red pixels will only be drawn where
// they overlap the existing blue pixels)
ctx.globalCompositeOperation='destination-over';
// second draw a red source rectangle
// (red will appear under the blue)
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Here's how to use compositing to change the color of your paw.
Clear the canvas. You cannot directly change the color of anything previously drawn on the canvas, so the typical workflow of canvas is to erase it and redraw items in their new positions & colors.
Draw the paw image.
Set compositing to source-atop so new drawings will only be drawn where the existing paw pixels exist.
Fill the canvas with your new paw color using fillStyle & fillRect. This causes your paw to be recolored because the newly colored rectangle pixels will only appear where your paw pixels currently exist.
Set compositing to destination-over so new drawings will be drawn under existing pixels.
Fill the yellow box. Your paw will not be overwritten because new (yellow) pixels will be drawn "under" your paw.
Set compositing back to the default source-over so new drawings will be drawn on top of existing drawings.
Draw your frame that's transparent in the center. Your paw and the yellow background will show through the transparent center of your frame.
Here's example code and a Demo:
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
var ctx2 = can.getContext('2d');
var images=[];
var urls=[];
urls.push('http://s7.postimg.org/aruxhs8mz/pink.png');
urls.push('http://s7.postimg.org/69smposl7/paw.png');
var imgCount=urls.length;
document.getElementById('recolor').onclick=function(){
redrawWithNewPawColor();
};
for(var i=0;i<urls.length;i++){
images[i]=new Image();
images[i].onload=myOnload;
images[i].src=urls[i];
}
function myOnload(){
imgCount--;
if(imgCount>0){return;}
start();
}
function start(){
redrawWithNewPawColor()
}
function drawWithRecoloredPaw(newPawColor){
// clear the canvas
ctx.clearRect(0,0,can.width,can.height);
// draw the paw
ctx.drawImage(images[1], 0, 0);
// set compositing to source-atop
// so only existing pixels will be overwritten
ctx.globalCompositeOperation='source-atop';
// fill with new color
ctx.fillStyle=newPawColor;
// Because of compositing, only the paw is being color filled
ctx.fillRect(0,0,can.width,can.height);
// set compositing to destination-over
// so new pixels will be draw behind existing (paw) pixels
ctx.globalCompositeOperation='destination-over';
// change the fill color to yellow
ctx.fillStyle='yellow';
// fill the yellow box
ctx.fillRect(40,0,250,300);
// set compositing to the default of source-over
ctx.globalCompositeOperation='source-over';
// draw the transparent frame
ctx.drawImage(images[0],0,0);
}
function redrawWithNewPawColor(){
drawWithRecoloredPaw(randomColor());
}
function randomColor(){
return('#'+Math.floor(Math.random()*16777215).toString(16));
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<button id='recolor'>Recolor Paw</button>
<br>
<canvas id="canvas1" width=600 height=600></canvas>
the problem is that canvas does not allow you to adjust an image, but you could create a block over the paw wich has an opacity of 0.5 and than fill in the transparent part again, you could also use this:
http://www.w3schools.com/tags/canvas_globalcompositeoperation.asp
If the image is in your own web folder you can use it with an img tag and get the data an change it
much like the W3S example Changing image in canvas
after this step it's just a matter of redrawing the canvas with the new data
This will not work if the image comes from another source, becaus of security reasons

how to make canvas outline a transparent png for on hover glow

Is it possible to give a glow effect to an image automatically, say using canvas?
jsfiddle
The canvas tag would have to omit the transparent
and make it have an outter glow?
<canvas id="canvas" width=960 height=960></canvas>
Make a canvas path glow by applying a series of overlapping shadows with increasing blur
A Demo: http://jsfiddle.net/m1erickson/Z3Lx2/
You can change the styling of the glow by varying the number of overlays and the blur size.
Example code for a glow effect:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// glow
var glowColor="blue";
ctx.save();
ctx.strokeStyle = glowColor;
ctx.shadowColor = glowColor;
ctx.shadowOffsetX=300;
for (var i = 0; i < 10; i++) {
ctx.shadowBlur = i * 2;
ctx.strokeRect(-270, 30, 75, 150);
}
ctx.restore();
To get the outline path of your phone image, you can use the "marching ants" algorithm.
This algorithm will create a path that outlines an image.
In your case you would define the image as all pixels that are not transparent.
Here's a very good implementation of "marching ants" that is used in the excellent d3 library:
https://github.com/d3/d3-plugins/blob/master/geom/contour/contour.js
It's used like this:
DrawImage your phone on the canvas.
// draw the image
// (this time to grab the image's pixel data
ctx.drawImage(img,0,0);
Get the pixel color array from the canvas using ctx.getImageData
// grab the image's pixel data
imgData=ctx.getImageData(0,0,canvas.width,canvas.height);
data=imgData.data;
Define a function that checks the pixel array for non-transparent pixels at any x,y on the canvas.
// This is used by the marching ants algorithm
// to determine the outline of the non-transparent
// pixels on the image
var defineNonTransparent=function(x,y){
var a=data[(y*cw+x)*4+3];
return(a>20);
}
Call the contour function:
// call the marching ants algorithm
// to get the outline path of the image
// (outline=outside path of transparent pixels
var points=geom.contour(defineNonTransparent);
Here's an example result:
the glow is automatically generated using overlapping shadows
the outline path of the phone is calculated using the marching ants algorithm

JS: Alter Color for specific image in HTML Canvas

Let say I got the sprite to the left and I want to apply a red filter over the image before drawing it in a canvas so it looks like the sprite to the right. http://puu.sh/6dQ0E.png
Is there a way to do that?
It could be done in two steps, but I don't know if it's possible to draw the geometric figure that has the exact same shape than the image. (Ex: Drawing a rectangle over it won't match.)
Note: I want the filter to only be applied to this image, not the whole canvas.
You can combine 2 filtering operations to draw the red filter only on top of your existing sprite.
globalCompositeOperation="source-atop" will cause new drawings to only draw on top of existing non-transparent pixels.
globalAlpha will make your new drawings semi-transparent.
Taken together, you can draw a semi-transparent filter that only fills where your non-transparent ghost exsits.
Then just draw a red rectangle over your existing sprite (the rectangle will only be visible inside the existing ghost).
ctx.drawImage(ghostSprites,0,0);
ctx.globalAlpha=0.62;
ctx.globalCompositeOperation="source-atop";
ctx.fillStyle="red";
ctx.fillRect(spriteX,spriteY,spriteWidth,spriteHeight);
Demo: http://jsfiddle.net/m1erickson/W4XrG/
From here...
Notice the black outlines of your sprite become washed from the red filter.
You could also use context.getImageData to grab only the black pixels of your ghost. Then redraw those black pixels over your red-filtered ghost so the black outlines are not washed. If you feel ambitious, you could give that a try!
Good luck with your project!
Here’s code
<style>
body{ background-color: ivory; padding:20px;}
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=start;
img.crossOrigin="anonymous";
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp0a.png";
function start(){}
var spriteX=0;
var spriteY=0;
var spriteWidth=133;
var spriteHeight=161
$("#recolor").click(function(){
ctx.drawImage(img,0,0);
ctx.globalAlpha=0.62;
ctx.globalCompositeOperation="source-atop";
ctx.fillStyle="red";
ctx.fillRect(spriteX,spriteY,spriteWidth,spriteHeight);
});
}); // end $(function(){});
</script>
</head>
<body>
<p>Before</p>
<img src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp0a.png">
<p>After</p>
<button id="recolor">Click to recolor the green sprite</button><br>
<canvas id="canvas" width=300 height=161></canvas>
</body>
</html>

Categories

Resources