I added a white border to a PNG image with Konvajs
konvajs link
I want to turn the transparent area inside the white border to white. So I want to paint the windows of the car white. (Red areas are transparent.)
Expected result:
To fix the problem you want background color white. As your link there are a function called removeTransparency it makes the image background color white see it.
// make all pixels opaque 100% (except pixels that 100% transparent)
function removeTransparency(canvas) {
var ctx = canvas.getContext('2d');
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var nPixels = imageData.data.length;
for (var i = 3; i < nPixels; i += 4) {
if (imageData.data[i] > 0) {
imageData.data[i] = 255;
}
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.putImageData(imageData, 0, 0);
return canvas;
}
One thing you could maybe do is converting you image to a SVG.
You would then have a path for your car windows with some class. You could afterward change the style of the SVG class in JS.
Related
When I try to draw an image on a canvas with a pre-loaded image with css like:
img.style.backgroundColor="red";
ctx.drawImage(img,0,0,100,100);
I find that the image is being drawn as it would appear without the css. Do HTML canvases support css? If not is there a way to overlay a png with transparency with a color only on the pixels that are not transparent?
Can you specify more about our requirement?
You can not set the background colour of an image which is going to draw directly from the canvas. if you altering a color, it will reflect in the source image.
You have to make it from the source element. If you want to fill the box size with some color, you could use fillStyle of the ctx before draw.
Have a look at the example here on w3schools that will get you started on how to load the image and copy it into the canvas. If you don't need the original image to be shown on the page then add 'style="display:none;"' to the img tag. To tint the image have a look at globalAlpha in combination with a filled rect - something along these lines:
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("scream");
ctx.globalAlpha = 0.5;
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#ff0000";
ctx.fill();
ctx.drawImage(img, 10, 10);
};
Canvas do not render css no matter what css you use for image it will always render plain image unless you draw border or background by yourself
It is possible to overlay the image drawn on a canvas by analyzing each pixel of the drawn image and then overlaying a 1x1 rectangle with the desired color. Here is an example of how to do so:
function overlayColor(img,x,y,a,r,g,b,drawWidth,drawHeight){
//create a canvas in memory and draw the image there to analyze it
var tmpCanvas = document.createElement('canvas');
var context = tmpCanvas.getContext('2d');
tmpCanvas.width = drawWidth;
tmpCanvas.height = drawHeight;
context.drawImage(img,0,0,drawWidth,drawHeight);
let pData = new Array();
for (let i = 0; i < tmpCanvas.width; i++){
for (let j = 0; j < tmpCanvas.height; j++){
if (context.getImageData(i, j, 1, 1).data[3] != 0){//if the pixel is not transparent then add the coordinates to the array
pData.push([i,j]);
}
}
}
//then every pixel that wasn't transparent will be overlayed with a 1x1 rectangle of the inputted color
//drawn at the (x,y) origin which was also given by the user
//this uses ctx, the context of the canvas visible to the user
ctx.fillStyle="rgba(" + r + "," + g + "," + b + "," + a + ")";
for (let i = 0; i < pData.length; i++){
ctx.fillRect(x + pData[i][0],y + pData[i][1],1,1);
}
}
Since the function takes an x and y value the image given by the user will be analyzed and overlayed only on pixels that are not transparent at the coordinate given by the user with the rgba value also given. I have found that this process can result in some lag but it could be overcome by saving the pData array and using the second half of the function to draw the array on screen again.
Can someone help me to fix the issue?
I want to apply alpha layer mask to make some of the image transparent using canvas.
Thanks a lot.
var redImageData = redCanvas.getContext("2d").getImageData(0, 0, 200, 200); //overlay
var ImageData = imageCanvas.getContext("2d").getImageData(0, 0, 200, 200);
var px = redImageData.data;
var px2 = ImageData.data;
for(var i = 0; i < px.length; i += 4) {
if(px[i + 0] == 0 && px[i + 1] == 0 && px[i+2] == 0){
px[i + 3] = 0;
} else {
px[i + 0] = px2[i + 0];
px[i + 1] = px2[i + 1];
px[i + 2] = px2[i + 2];
px[i + 3] = px2[i + 3];
}
}
ctx.imageSmoothingEnabled = true;
ctx.putImageData(redImageData, 0, 0);
alpha mask overly https://i.stack.imgur.com/zCzOf.png
The linked image is not really an alpha mask but a matte image. The difference is that a matte image represent what would be an alpha-channel but showing it as RGB (or gray-scale) components. It doesn't actually have an alpha-channel. Matte images are common in video-compositing software but not so useful on web.
Canvas, or the Porter-Duff methods it uses, does not support matte images directly so they have to first be converted to an actual alpha-channel. To do this you have to iterate over each pixel and move one of the component values (from red, green or blue - doesn't matter which) into the alpha-channel.
When that is done you can use the canvas object that now has proper alpha-channel with composite operations which only uses the alpha information (blending modes is a different chapter).
The better approach is of course to provide the images as PNG with a proper alpha channel. But in any case, to show it's possible to also work with matte images, although not as efficient, we can do the following:
Converting matte image into alpha channel
First step: this code section shows how you can efficiently do the pre-step of converting the matte image into an alpha channel. The resulting colors are not important for the main compositing step as it will only use the alpha-channel, as already mentioned.
Just make sure the image has loaded properly before trying to use the image by either using the image's onload callback or running the script after everything has loaded.
This code will simply shift a component (blue in this case) using the full 32-bit value of the pixel (for efficiency) into the alpha-channel which leaves the image looking cyan but with proper alpha as you can see with the orange background showing through (most of the code is to handle loading and setup though).
window.onload = function() {
// at this stage the image has loaded:
var img = document.getElementById("img");
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
// - setup canvas to match image
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
// - draw image
ctx.drawImage(img, 0, 0);
// CONV. STEP: move a component channel to alpha-channel
var idata = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data32 = new Uint32Array(idata.data.buffer);
var i = 0, len = data32.length;
while(i < len) {
data32[i] = data32[i++] << 8; // shift blue channel into alpha (little-endian)
}
// update canvas
ctx.putImageData(idata, 0, 0);
};
body {background: #f72; font-size:44px; color:rgba(0,0,0,0.5)}
<img id="img" crossorigin="anonymous" src="https://i.imgur.com/QRGYuWg.png"> ►
<canvas id="c"></canvas>
Compositing
The second part then becomes about compositing using the new alpha-channel.
In this case the matte image's black becomes fully transparent while the white becomes fully opaque. You could have reversed this in the conversion step but it does not really matte as long as you're aware of the how the matte image looks.
To replace the interior content we use compositing mode source-in. This will replace the content depending on the alpha value while keeping the alpha-channel as it is.
Dealing with the interior part first using the same mode allows us to do additional things with the content before drawing the frame (think vignette, shadows etc.).
As the final step we fill in the transparent areas, the frame itself, by using the composite mode destination-over which replaces the more transparent ares with the content being drawn to canvas (conceptually it draws "behind" the existing content).
The code below uses simple colored boxes - just replace those with whatever you want to draw.
window.onload = function() {
var img = document.getElementById("img");
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);
var idata = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data32 = new Uint32Array(idata.data.buffer);
var i = 0, len = data32.length;
while(i < len) data32[i] = data32[i++] << 8;
ctx.putImageData(idata, 0, 0);
// COMP. STEPS: use the mask with composite operation. Since our frame
// is black (= transparent as alpha) we can use the following mode:
ctx.globalCompositeOperation = "source-in";
// draw something, here a blue box, replace with whatever you want
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// to fill the frame area, still transparent, use this mode:
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = "yellow";
ctx.fillRect(0, 0, canvas.width, canvas.height);
};
body {background: #f72; font-size:44px; color:rgba(0,0,0,0.5)}
<img id="img" crossorigin="anonymous" src="https://i.imgur.com/QRGYuWg.png"> ►
<canvas id="c"></canvas>
I have a large amount of images with a black background, here is one for example:
Is it possible through Javascript to have to ignore the black (#000000) and have it draw on canvas? to appear like this?
Basically trying to take the black pixels and make it an alpha channel.
So you'll need to run through all the pixels and change the alpha value of all the black pixels.
https://jsfiddle.net/0kuph15a/2/
This code creates a buffer (empty canvas) to draw the original image to. Once thats done, it takes all the pixels of this buffer canvas and then iterates over all the pixels and checks their values. I add up the Red, Blue, and Green values to see if they are less then 10 (just incase some pixels aren't pure black 0), but would visually appear black to the human eye. If it is less then 10 it simply turns the alpha value of that pixel to 0.
var canvas = document.getElementById('main');
var ctx = document.getElementById('main').getContext('2d');
var tile = new Image();
tile.src = document.getElementById('image').src;
tile.onload = function() {
draw(tile);
}
function draw(img) {
var buffer = document.createElement('canvas');
var bufferctx = buffer.getContext('2d');
bufferctx.drawImage(img, 0, 0);
var imageData = bufferctx.getImageData(0,0,buffer.width, buffer.height);
var data = imageData.data;
var removeBlack = function() {
for (var i = 0; i < data.length; i += 4) {
if(data[i]+ data[i + 1] + data[i + 2] < 10){
data[i + 3] = 0; // alpha
}
}
ctx.putImageData(imageData, 0, 0);
};
removeBlack();
}
You can easily change this line if(data[i]+ data[i + 1] + data[i + 2] < 10){ to if(data[i]+ data[i+1] + data[i+2]==0){ if you know for a fact only #000 blacks are used.
You can accomplish that using blend modes.
Change the context globalCompositeOperation to screen, and you can get that result. Here's an example:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var image = new Image();
image.src = "https://images.blogthings.com/thecolorfulpatterntest/pattern-4.png";
image.onload = function() {
context.drawImage(image, 0, 0, canvas.width, canvas.height);
var blackImage = new Image();
blackImage.src="http://www.printmag.com/wp-content/uploads/Sillitoe-black-white.gif";
blackImage.onload = function(){
context.globalCompositeOperation = "screen";
context.drawImage(blackImage, 0, 0, canvas.width, canvas.height);
}
};
<canvas id="canvas" width="300" height="250"></canvas>
<hr/>
<h1>Images used:</h1>
<img src="https://images.blogthings.com/thecolorfulpatterntest/pattern-4.png"/>
<img src="http://www.printmag.com/wp-content/uploads/Sillitoe-black-white.gif"/>
How about saving the picture as an .svg file...from there you can change all colors and other settings
Felipe's answer addressed my issue. Alpha pixel manipulation does not work
(eg, setting every 4th pixel to 0) for preserving alphatransparency with multiple images added into the same context at the same time.
eg:
this.ctx1.putImageData(output, 0, 0); // without setting the context's globalCompositeOperation, this image is written over by the next putImage operation
this.ctx1.putImageData(output, 20, 0);
Go here to review the blending options. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
I tried to create an HTML canvas, place a rectangle... then INSIDE THAT rectangle, draw various shapes and an RGBa PNG... all of them clipped inside the dimensions of the rectangle. Then I tried to change the color of the PNG when you press an HTML button input. (Further comments in code.)
Heres the problems... A. You have to draw to a temporary canvas and apply "GlobalCompositeOperation source-atop" just AFTER the clipping rectangle. Everything drawn after that is successfully clipped into the rect shape. Then the whole thing is copied (drawn) to a MAIN canvas. I was told to do it this way in order for the programming to recognize MULTIPLE elements after a "composite" operation. I have to say this works BEAUTIFULLY!! but here's problem B...
To to a "getData" on an image (to change color), I think you have to place the image on a canvas, and doing all the image pixel manipulation screws up the "composite" operation, so I tried to draw the PNG to a THIRD canvas, do the pixel changes, and then draw it to the temporary canvas... adding it to the rest of the elements....THEEENNN draw it all to the main canvas. Does not work. See code. Please help, Im mad enough to chew neutronium.
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<canvas id="theCanvas" width="200" height="200" style="border:2px solid #000000;"></canvas>
<canvas id="tempCanvas" width="200" height="200" style="display:none;"></canvas>
<canvas id="anotherCanvas" width="200" height="200" style="display:none;"></canvas>
<form>
<input type="button" id="changeColor" value="Click to Change Color of Graphic">
</form>
<script type="text/javascript" src="hereWeGoAgain_GHELP.js"></script>
</body>
</html>
//------------------------------------- JS
window.addEventListener("load", eventWindowLoaded, false);
function eventWindowLoaded () {
canvasApp();
}
function canvasApp() {
var canvas = document.getElementById('theCanvas');// the main canvas, where it all goes in the end
var context = canvas.getContext('2d');
var tempCanvas = document.getElementById('tempCanvas');// the canvas to do my "source-atop" stuff...
var tempContext = tempCanvas.getContext('2d');
var anotherCanvas = document.getElementById('anotherCanvas');
var anotherContext = anotherCanvas.getContext('2d');
// ...and Im thinking I should draw the RGBA PNG here, before placing it in the temp canvas, with the other elements
var cc = document.getElementById('changeColor');
cc.addEventListener('click', function(){changeColorFunction('ff0000');}, false);
// the HTML form button to change the PNG color
var colorOfThePlacedPNG = "#000000";
var imagesToLoad = 0;
var imagesLoaded = 0;
function drawScreen() {
tempContext.fillStyle="#999999";
tempContext.fillRect(0,0,200,200); //color the whole temp canvas area grey....
tempContext.fillStyle="#2baae1";
tempContext.fillRect(30,30,140,140);//now draw a light blue rect inside....
tempContext.globalCompositeOperation="source-atop"; // now make everything drawn AFTERWARDS be clipped (masked) inside the blue rect
// when I comment out the above "global Comp Op"... everything draws to the main canvas normally...just not clipped(masked) however
tempContext.fillStyle="#f47e1f";
tempContext.fillRect(150,100,150,150);//SO heres an orange box intentionally clipped off the bottom right in the blue rect
tempContext.fillStyle="#d89bc5";
tempContext.fillRect(40,50,80,200);//AND heres a light purple rect intentionally clipped at the bottom of the blue rect
getTheImageData(); //draw PNG to another canvas, convert image data, put in tempContext
//tempContext.restore();//dont know if I need this
context.drawImage(tempCanvas, 0, 0);// and then FINALLY draw all to the main canvas
}
var loaded = function(){
imagesLoaded += 1;
if(imagesLoaded === imagesToLoad){
drawScreen();
}
}
var loadImage = function(url){
var image = new Image();
image.addEventListener("load",loaded);
imagesToLoad += 1;
image.src = url;
return image;
}
function changeColorFunction(e) {
colorOfThePlacedPNG = e;
drawScreen();
}
function getTheImageData(){
anotherContext.drawImage(testPNGimage, 0, 0);// draw to the third canvas(another canvas)
var imgData = anotherContext.getImageData(0, 0, 200, 200);
// how do i color it red? ....like #ff0000 ???
var i;
for (i = 0; i < imgData.data.length; i += 4) {
imgData.data[i] = 255 - imgData.data[i];
imgData.data[i+1] = 255 - imgData.data[i+1];
imgData.data[i+2] = 255 - imgData.data[i+2];
imgData.data[i+3] = 255;
}
tempContext.putImageData(imgData, 0, 0);
}
var testPNGimage = loadImage("test.png");// the PNG is just a 75X75px black squiggle drawn in pshop
}
You're overcomplicating things!
There is a clipping method built into canvas.
Use clipping instead of compositing and multiple canvases.
Do this and all new drawings will be clipped inside your 140x140 rect:
context.beginPath();
context.rect(30,30,140,140);
context.clip();
Here's a simplified redesign of your code:
Draw a grey rect filling the canvas.
Draw a blue 140x140 rect at [30,30].
Clip all new drawings into the blue rect with context.clip()
Draw a clipped orange rect.
Draw a clipped purple rect.
Unclip so new drawings will be visible anywhere on the canvas.
Draw the squiggle image (it's not clipped).
Use .getImageData to invert every pixel's color.
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="https://dl.dropboxusercontent.com/u/139992952/multple/squiggle.png";
function start(){
//color the whole canvas area grey....
ctx.fillStyle="#999999";
ctx.fillRect(0,0,200,200);
//now draw a light blue rect inside....
ctx.fillStyle="#2baae1";
ctx.beginPath();
ctx.rect(30,30,140,140);
ctx.fill();
// save the unclipped context state
ctx.save();
// cause all new drawings to be clipped inside
// the blue 140x140 rect at [30,30]
ctx.clip();
//SO heres an orange box intentionally clipped off the bottom right in the blue rect
ctx.fillStyle="#f47e1f";
ctx.fillRect(150,100,150,150);
//AND heres a light purple rect intentionally clipped at the bottom of the blue rect
ctx.fillStyle="#d89bc5";
ctx.fillRect(40,50,80,200);
// restore the context state (releases clipping for new drawings)
ctx.restore();
// draw the squiggley line image -- it's not clipped in the blue rect
ctx.drawImage(img,0,0);
// invert the colors using getImageData
var imgData = ctx.getImageData(0, 0, 200, 200);
var i;
for (i = 0; i < imgData.data.length; i += 4) {
imgData.data[i] = 255 - imgData.data[i];
imgData.data[i+1] = 255 - imgData.data[i+1];
imgData.data[i+2] = 255 - imgData.data[i+2];
imgData.data[i+3] = 255;
}
ctx.putImageData(imgData, 0, 0);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=200 height=200></canvas>
if i have an image (jpg, png, gif .doesn't really matter ) and i want to have some javascript code change part of an image.
for example, lets say i have this picture of a cookie like here.
and i have a color picker that lets a person select a color. I want to change the color of part of the image (in this case, lets say the color of the chocolate chips) to the color that is picked.
is that possible to do in javascript / jquery ?
Its possible with Javascript and the canvas element by directly manipulating the pixel data. The below example turns blue into red.
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
image = document.getElementById("testImage");
canvas.height = canvas.width = 45;
ctx.drawImage(image,0,0);
var imgd = ctx.getImageData(0, 0, 45, 45),
pix = imgd.data;
for (var i = 0, n = pix.length; i <n; i += 4) {
if(pix[i + 2] > 20){ // Blue threshold
// Swap red and blue component values.
var redVal = pix[i]; // Copy the current red component value
pix[i] = pix[i + 2]; // Assign the current blue component value to red
pix[i+2] = redVal; // Assign the old red value to blue.
}
}
ctx.putImageData(imgd, 0, 0);
Its not very fast to do it this way however, and for larger images you would see a noticeable performance drop depending on the browser. AS for jQuery, there is nothing related to this that jQuery provides that would help.