How to make the black part of the background image transparent? - javascript

Here's the result I want to realize.As is shown, the QR Code consists of black and white , but I want the black part to be transparent so as to blend with the background outside. The background outside may change, it's not always green.
I've tried to use background blend mode with CSS like this:
background-image: url('');
background-color: rgba(0,0,0,0);
background-blend-mode: screen;
It does nothing.
Thx for help.

You can actually achieve this effect through canvas pixel manipulation, but as other users have stated it would be simpler to make your image transparent through an image editing program.
I have created an example of how to achieve this in the fiddle here.
What this code first does is retrieve the image element <img id="code" src="url.png">, construct a canvas object and draw the contents of the image into the canvas through the context.drawImage call.
// Get the image element.
var el = document.getElementById('code');
// Create a canvas and set its width and height.
var canvas = document.createElement('canvas');
canvas.setAttribute('width', 250);
canvas.setAttribute('height', 250);
// Get the canvas drawing context, and draw the image to it.
var context = canvas.getContext('2d');
context.drawImage(el, 0, 0, canvas.width, canvas.height);
Next,
// Get the canvas image data.
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
// Create a function for preserving a specified colour.
var preserveColor = function(imageData, color) {
// Get the pixel data from the source.
var data = imageData.data;
// Iterate through all the pixels.
for (var i = 0; i < data.length; i += 4) {
// Check if the current pixel should have preserved transparency. This simply compares whether the color we passed in is equivalent to our pixel data.
var preserve = data[i] === color.r
&& data[i + 1] === color.g
&& data[i + 2] === color.b
&& data[i + 3] === color.a;
// Either preserve the initial transparency or set the transparency to 0.
data[i + 3] = preserve ? data[i + 3] : 0;
}
return imageData;
};
// Get the new pixel data and set it to the canvas context.
var newData = preserveColor(imageData, {r: 255, g: 255, b: 255, a: 255});
context.putImageData(newData, 0, 0);
Finally, we add the element to our page using document.body.appendChild(canvas);. You can alternatively completely replace the original image element with the canvas.
Obviously, this isn't an ideal solution because iterating through pixel data can be very slow and only increases with large image sizes. You could also easily change the preserveColor function to instead do the opposite with makeTransparent where the color specified becomes transparent. However, this would require a bit more logic for colors that are similar to the one you specify. Alternatively, you could implement this as a shader so that the processing is more efficient by shifting it to the GPU instead of the CPU.

You have the correct blend mode, and I assume the background-image url is omitted on purpose, so the first reason I would resort to is browser compatibility. For this, take a look at the compatibility chart on caniuse.com, for example.
I'd suggest using a transparent PNG here, as that's exactly what it's indended for. Even if you get background-blend-mode to work, unless the background is perfectly black (meaning #000) - looking at your example image, that isn't the case, so you wouldn't get the effect you want one way or the other.
Another issue I can think of here is that you probably want that QR code as in img element, for the following reasons:
a user may want to save the image in the intended way
background images don't render as block elements, so depending on your layout, they may be easily overlapped
background images often get discarded when printing pages, so they shouldn't be used for content
screen readers will likely ignore backgound images, assuming they're just design

Related

Weird HTML 5 Canvas Antialiasing

I've been playing with canvas element and discovered that when I attempt to draw NxN uniform solid-colored cells next to each other, in some width/height configurations, there are blurry white-ish lines between them.
For instance, this canvas is supposed to look black but contains some sort of grid which I conjecture to be a result of faulty antialiasing in the browser.
Suffice to say, this bug appears only in some configurations but I would like to get rid of it for good. Is there any way to circumvent this? Have you ever had problems with antialiasing in canvas?
I have made this fiddle which demonstrates the issue and allows you to play with the dimensions of the canvas and number of cells. It also contains the code I use to draw the cells, so that you can inspect it and tell me if I'm doing anything wrong.
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
for (var i = 0; i < numberOfCells; ++i) {
for (var j = 0; j < numberOfCells; ++j) {
ctx.fillStyle = '#000';
ctx.fillRect(j * cellWidth, i * cellHeight, cellWidth, cellHeight);
}
}
Thanks in advance,
Petr.
jsFiddle : https://jsfiddle.net/ngxjnywz/2/
snippet of javascript
var cellWidth = Math.ceil(canvasWidth / numberOfCells);
var cellHeight = Math.ceil(canvasHeight / numberOfCells);
Depending on the width, height and your numberOfCells you are sometimes getting a... lets say 4.2 which is 4, however this would be displayed wrong and will allow a 1 pixel blank line to appear. So all you need to do is use the Math.ceil function and this will cause your cellWidth and cellHeight to always be the higher number and you won't get blank lines anymore
The best solution is to add a 0.5 pixel wide stroke around all the fills, using the same style as the fill and offsetting all drawing so that you render at the center of pixels rather than the top left.
If you add scaling or translation you will have to adjust the coordinates so that you still give the centers for your drawing coordinates.
In the end you can only reduce the artifacts but for many situations you will not be able to completely remove them.
This answer shows you how to remove the artifacts for an untransformed canvas.
How to fill the gaps
After reading through and trying several approaches, I've decided to come up with my own. I've created another (virtual) canvas which had integer dimensions corresponding to the number of cells in the grid.
After drawing all the cells in there, I call context.drawImage() on the main canvas and pass the virtual canvas as an argument along with offset and scale parameters to make it fit rest of my drawing. Assuming that the browser would scale the virtual canvas's image as a whole (and not as individual cells), I was hoping to get rid of the unwanted separator lines.
In spite of my efforts, the lines are still there. Any suggestions?
Here's the fiddle demonstrating my technique: https://jsfiddle.net/ngxjnywz/5/

Very simple javascript ocr on black text white background

I have a super simple need of OCR.
My application allows creating image from text. Its very simple. People choose a font face, bold or not, and size.
So they get outputs like this, ignoring the border:
I wanted to create a very simple OCR to read these. I thought of this approach:
In the same way I generate an image for the message. I should generate an image for each character. Then I go through and try to match each character image to the black occourances in the canvas. Is this right approach?
The method I use to draw element to image is this copy paste example here: MDN :: Drawing DOM objects into a canvas
Ok, another couple of tries...
Another method that's simpler than OCR: use Steganography to embed the text message as part of the image itself. Here's a script that uses the alpha channel of an image to store text: http://www.peter-eigenschink.at/projects/steganographyjs/index.html
You can try this "home brewed" OCR solution...but I have doubts about its effectiveness.
Use the clipping form of context.drawImage to draw just the message-text area of your image on the canvas.
Use context.getImageData to grab the pixel information.
Examine each vertical column starting from the left until you find an opaque pixel (this is the left side of the first letter).
Continue examining each vertical column until you find a column with all transparent pixels (this is the right side of the first letter).
Resize a second canvas to exactly contain the discovered letter and drawImage just the first letter to a second canvas.
Set globalCompositeOperation='destination-out' so that any new drawing will erase any existing drawings where the new & old overlap.
fillText the letter "A" on the second canvas.
Use context.getImageData to grab the pixel information on the second canvas.
Count the opaque pixels on the second canvas.
If the opaque pixel count is high, they you probably haven't matched the letter A, so repeat steps 5-9 with the letter B.
If the opaque pixel count is low, then you may have found the letter A.
If the opaque pixel count is medium-low, you may have found the letter A but the 2 A's are not quite aligned. Repeat steps 5-9 but offset the A in step#7 by 1 pixel horizontally or vertically. Continue offsetting the A in 1 pixel offsets and see if the opaque pixel count becomes low.
If step#12 doesn't produce a low pixel count, continue with the letter B,C,etc and repeat steps 5-9.
When you're done discovering the first letter, go back to step#1 and only draw the message-text with an offset that excludes the first letter.
OCR is always complex and often inaccurate.
I hate to wave you off of a solution, but don't use OCR for your purpose
Simple and effective solution...
Put your message in the image's file name.
Solution found - GOCR.js - https://github.com/antimatter15/gocr.js/tree/d820e0651cf819e9649a837d83125724a2c1cc37
download gocr.js
decide if you want to go from WebWorker, or mainthread
worker
In the worker put this code:
importScripts(gocr.js)
GOCR(aImgData)
where aImgData is, take an image, load it, draw it to canvas, then send the data to the webworker. (see mainthread method)
mainthread
<script src="gocr.js">
<script>
var img = new Image()
img.onerror = function() {
console.error('failed')
}
img.onload = function() {
var can = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
can.width = img.width;
can.height = img.height;
var ctx = can.getContext('2d')
ctx.drawImage(img, 0, 0)
// to use this in a worker, do ctx.getImageData(0, 0, img.width, img.height), then transfer the image data to the WebWorker
var text = GOCR(can);
}
</script>

How to get color that is behind a transparent element?

I am working on a site where the body has many color. When the content is scrolled down the background of the content changes.
In the below image u can see body text with blue color background. So when the content is scrolled down with the scrollbar then the background image should also scroll along with the content.
So what happens is when scroll bar stops at a color like blue then automatically the menu header(Home) background should change to blue.
First of all we should know what we want to do. Step one is actually get a color of the image, thus acces a property of the image.
However the browser cannot get direct information of the image itself(You can, of course, get information from the element of the image). However one webbrowser drawing method let us control every aspect of a image: Canvas.
So first we have to convert our image to an canvas element.
This is fairly simply done with drawImage(img, posx, posy, canvasWidth, canvasHeight);
We will also stretch the canvas image + the element to the entire screen. The reason we want to define to size also at the canvas is because this way the canvas will calculate every new generated pixel, so we can acces every of the new pixels:
var screenWidth = window.innerWidth,
screenHeight = window.innerHeight,
c = document.createElement('canvas');
ctx = c.getContext("2d"),
img = document.getElementById("image");
c.width = screenWidth;
c.height = screenHeight;
ctx.drawImage(img, 0, 0, screenWidth, screenHeight);
$(c).attr('id', 'c');
$('body').prepend(c);
$(img).remove();
An Canvas element as is dynamically made and will fit to the screen size(Note that this is only at first start of the webbrowser. So when you resize the canvas will not resize with the screen).
We remove the image as it is no use anymore. It's a good practive to transform from image to canvas in my opinion. As you will be certain that the image will be loaded in the browser first and then transformed to a canvas element.
Next we're going to acces a pixel. You can acces pixel data with getImageData(offsetX, offsetY, sizeX, sizeY).data; And RGBA color array will be returned.
Now the offsetX and offsetY will be the offset of the element color picker. Note that this elements offset should be relative to the webbrowsers viewport as the background is fixed You can do this with getBoundingClientRect();
The size is just 1x1 as we want 1 pixel.
This all should happen when the user is scrolling, you can catch the scroll event with .scroll(function); :
$(window).scroll(function () {
var offset = document.getElementById('color').getBoundingClientRect(),
offsetX = offset.left,
offsetY = offset.top;
var color = document.getElementById('c').getContext('2d').getImageData(offsetX, offsetY, 1, 1).data;
$('header').css('background-color',"rgba("+ color[0] + ', ' + color[1] + ', '+ color[2] + ', ' + color[3] + ")");
});
Where we add the rgba array colors just with the first 4 indexes(Because and rgba collor pattern never has more as 4 values) with plain css to the header element.
Loading External image into a canvas
When you want to transform an external image to a canvas element you might get this error:
Cross-origin image load denied by Cross-Origin Resource Sharing policy.
It is for security reasons that you can't fully acces this image.
As explained here: HTML 5 canvas getImageData from an externally-loaded image
There are some external hosts that supports this, for example dropbox.
Local
However when you just store it locally it will just work fine.
jsFiddle
This method was tested in chrome, IE and firefox.
I want to use those external (dropbox) files
I'm not sure why this happens but for some reason with this method there are still some security diffecults. To enable this CORS you have to add a image property .crossOrigin = "Anonymous";. However when you load the image with HTML the the element has been made without this property. So you should assign this property when the image is created:
var canvas = document.getElementById("c"),
ctx = canvas.getContext("2d"),
screenWidth = window.innerWidth,
screenHeight = window.innerHeight;
// Using image WITH crossOrigin=anonymous
// Succeeds in Chrome+Mozilla, Still fails in IE
var externalImage2 = new Image();
externalImage2.onload = function(){
canvas.width= screenWidth;
canvas.height= screenHeight;
ctx.drawImage(externalImage2, 0, 0, screenWidth, screenHeight);
// this will FAIL on a CORS violation
}
externalImage2.crossOrigin = "Anonymous";
externalImage2.src="https://dl.dropboxusercontent.com/s/8g8lgmdx341j1d6/rainbow_gradient_horizontal.jpg";
This is just an picture i uploaded to my dropbox where i just shared the photo. Note:
The way to convert a share link to a direct link is to change the domain from "www.dropbox.com" to "dl.dropboxusercontent.com". See https://www.dropbox.com/developers/blog/53/programmatically-download-content-from-share-links.
source: Cross-origin image load from cross-enabled site is denied
This creates an image and puts it into an excisting canvas element. So note that you should have a canvas element into your HTML.
jsFiddle
This method does only work for chrome and firefox as other browsers still have some security issues.
Additional note is the pixel that gets captured is the top-left pixel of the 'color-pick' element. You can adjust this offset if you want it to catch, for example, the middle pixel of the element.
This was a very interesting question. I hope it helped!
well, you may give background color of the home bar as transparent if you have no band between the home and the content.
.home{
background:transparent;
}

Switch canvas context

Assuming that I have a WebGL canvas (by calling getContext("experimental-webgl")).
Is there any way to switch context later for using a "2d" one ?
The goal of such thing would be to display a debug BSOD-like when an error happening during rendering.
If it's not possible, then :
Can I embed an html element over a canvas, and force this element to have exactly the same same that the canvas (even if this last is resized) ?
Can I replace an dom node, and update every reference about the old one to reflect the changement ?
[edit] This is my current minimal call code. Canvas is a DOM node containing a canvas which is filled by WebGL API, and callback is a function which process a single frame.
function failure(cvs, e) {
var ctx = cvs.getContext('2d'); // Fail here, returns `null' if cvs.getContext('webgl') has been called
ctx.fillStyle = 'rgb(0, 0, 0)';
ctx.fillRect(0, 0, cvs.width, cvs.height);
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.font = 'bold 12px sans-serif';
ctx.fillText(e.toString(), 0, 0);
}
function foobar(canvas, callback) {
try {
callback();
} catch (e) {
failure(canvas, e);
throw e;
} finally {
requestAnimationFrame(arguments.callee);
}
}
The short answer is pretty much no, according to the spec.
Every canvas has what is called a primary context. This is the first context that is invoked on a canvas. Making a non-primary context on a canvas might do some things on different browsers but I would never, ever depend on it.
I would instead have a second canvas that is overlaid over the first and maintains the same width and height attributes. I would then hide one and unhide the other (or just unhide the 2D one when you want it seen).
OR just use a PNG for simplicity's sake., centered inside of a DIV that also holds the canvas. In other words:
Div container has black background and holds:
PNG (centered)
3D Canvas
Then when you want the error png to be displayed you just hide the 3D canvas (and optionally unhide the PNG)
Rather than have two canvases overlaying, the solution I went with was to replace the existing canvas with a clone of itself.
var newCvs = cvs.cloneNode(false);
cvs.parentNode.replaceChild(newCvs, cvs);
cvs = newCvs;
All the properties of the original canvas will be retained but the context will be freed up to allocate as you wish.

canvas - layer small image on larger background image, merge to single canvas pixel data

How do I merge a smaller image on top of a larger background image on one canvas. The smaller image will move around. So I will need to keep reference of the original background pixel data, so that the each frame the canvas can be redrawn, with the overlay in its new position.
BgImage: 1280 x 400, overlayImage: 320 x 400, overlayOffsetX: mouseX
I think it is common to draw whole scene each time you want to change something, so:
context.drawImage(bgImage, 0, 0);
context.drawImage(overlayImage, overlayOffsetX, 0);
UPDATE
You could manually compose image data of two images with making copy of background image data
or
do something easier, probably faster. You could create new canvas element (without attaching to the document) which would store image data in easy to manage form. putImageData is good if you want to place rectangular image into the canvas. But if you want to put image with transparency, additional canvas will help. See if example below is satisfying you.
// preparation of canvas containing data of overlayImage
var OVERLAY_IMAGE_WIDTH = 320;
var OVERLAY_IMAGE_Height = 400;
var overlayImageCanvas = document.createElement('canvas');
overlayImageCanvas.width = OVERLAY_IMAGE_WIDTH;
overlayImageCanvas.height = OVERLAY_IMAGE_HEIGHT;
overlayImageCanvas.getContext('2d').putImageData(overlayImage, 0, 0);
// drawing scene, execute this block every time overlayOffsetX has changed
context.putImageData(bgImage, 0, 0);
context.drawImage(overlayImageCanvas, overlayOffsetX, 0);

Categories

Resources