HTML5 Canvas globalCompositeOperation lighter and source-atop at same time - javascript

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>

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 - change color of previously drawn shapes

I have various functions on my canvas that allow it to be drawn on. However, when lines overlap and get drawn over previously drawn spots, I want the color of the spot that was re-drawn over to change color to a darker shade. What would be the code I would write to evaluate whether or not a spot was drawn over?
You can use "Blending" to darken overlapping areas on your canvas
The advantage of using blending is that you don't have to keep track of previously drawn strokes & fills.
If you don't care about supporting Internet Explorer/Edge, you can use context.globalCompositeOperation="multiply" which will darken by applying a multiply filter on new overlapping drawings. (P.S. to Microsoft: Come on MS...give us more good stuff like Blending!).
// For browser except IE/Edge...
// darken overlapping strokes using "multiply" compositing
ctx.fillStyle='skyblue';
ctx.fillRect(100,100,100,100);
ctx.globalCompositeOperation='multiply';
ctx.fillRect(75,150,100,100);
For IE/Edge, there is a "manual" multiply filter which involves reading each pixel's RGBA information using context.getImageData and doing the following computation against every red, green & blue color value. This manual method is considerably slower than using compositing.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// rgb values for skyblue
var r=135;
var g=206;
var b=235;
multiplyFilter(100,100,100,100,r,g,b);
multiplyFilter(75,150,100,100,r,g,b);
function multiplyFilter(x,y,width,height,newRed,newGreen,newBlue){
var imgData=ctx.getImageData(x,y,width,height);
var data=imgData.data;
for (var i=0;i<data.length;i+=4) {
if(data[i+3]>0){
data[i+0] = (data[i+0]*newRed)/255;
data[i+1] = (data[i+1]*newGreen)/255;
data[i+2] = (data[i+2]*newBlue)/255;
}else{
data[i+0]=newRed;
data[i+1]=newGreen;
data[i+2]=newBlue;
data[i+3]=255;
}
}
ctx.putImageData(imgData,x,y);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Darken overlaps using a "manual" multiply filter</h4>
<canvas id="canvas" width=300 height=300></canvas>

Fabric JS pixel by pixel manipulation

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>

JavaScript either strokeRect or fillRect blurry depending on translation

Earlier on I noticed that strokeRect (and any other method that involved stroke such as lineTo) created a grey 2 px wide line instead of a 1px wide black line. After some Google searching I found that context.translate(0.5, 0.5) fixed this. but now fillRect (and like before any other method that involves fill) creates a black box with a grey border around it.
Does anyone know a good way to make it so that both fillRect and strokeRect have crisp edges with no grey borders? I also don't know whether or not I should use context.translate(0.5, 0.5) for images, as it seems like SVGs have crisp edges regardless of whether or not I translate.
Here is a jsfiddle demonstrating this: http://jsfiddle.net/Tysonzero/ydm21pkt/1/
Note that the bottom strokeRect is crisp while the top one is blurry, and the top fillRect is crisp while the bottom one is blurry.
Strokes draw half-inside & half-outside the x,y coordinates. That's why you are seeing the blur with integer x,y and why it clears up when the x,y are offset by a half pixel. Here's more on why the blur occurs: http://www.mobtowers.com/html5-canvas-crisp-lines-every-time/
An easy way to make rects crisper is to add methods to your context instance that offset strokeRect & fillRect for best appearance:
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// add pixel aligned versions of strokeRect & fillRect to this context instance
context.sRect=function(x,y,w,h){
x=parseInt(x)+0.50;
y=parseInt(y)+0.50;
this.strokeRect(x,y,w,h);
}
context.fRect=function(x,y,w,h){
x=parseInt(x);
y=parseInt(y);
context.fillRect(x,y,w,h);
}
context.strokeStyle = "#000000";
context.fillStyle = "#000000";
context.strokeRect(100, 50, 100, 100);
context.fillRect(300.5, 50.5, 100, 100);
context.sRect(100,200,100,100);
context.fRect(300.5,200,100,100);
context.fillText('Unadjusted',20,100);
context.fillText('Adjusted',20,250);
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=500 height=500></canvas>

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