Is there any way to convert the image so it appears as a gray scale. Changing rgb is not working as required.
As you probably know, screen color consists of 3 components: red, green and blue. Each component or color (for example, red) has a value from 0 to 255. The value 0 indicates there is no red color and the value 255 would be the brightest possible red color. To convert a color to grayscale you just need to calculate the average for all three components. This can be done using the simple formula below:
grayscalecolor = (red + green + blue) / 3;
Canvas Method
var canvas = document.createElement('canvas');
var canvasContext = canvas.getContext('2d');
var imgW = imgObj.width;
var imgH = imgObj.height;
canvas.width = imgW;
canvas.height = imgH;
canvasContext.drawImage(imgObj, 0, 0);
var imgPixels = canvasContext.getImageData(0, 0, imgW, imgH);
for(>var y = 0; y < imgPixels.height; y++){
for(>var x = 0; x < imgPixels.width; x++){
var i = (y * 4) * imgPixels.width + x * 4;
var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
imgPixels.data[i] = avg;
imgPixels.data[i + 1] = avg;
imgPixels.data[i + 2] = avg;
}
}
canvasContext.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height);
return canvas.toDataURL();
Microsoft Internet Explorer does not support the CANVAS tag, but Microsoft does provide image manipulation through filters. The filter to use for converting images to grayscale is the filter named DXImageTransform.Microsoft.BasicImage.
imgObj.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(grayScale=1)';
If you're able to use HTML5 you could use a canvas, as seen here.
Google for "canvas grayscale image" the first link will be:
HTML 5 introduces Canvas object which can be used to draw and manipulate images. In this example, I used a Canvas to turn an image into grayscale. If your browser supports Canvas, you should see two images above: the normal image and the grayscale one. The grayscale one is not a separate image, it is generated by utilizing Canvas.
http://permadi.com/tutorial/jsCanvasGrayscale/index.html
If you want this to be done client side (on the browser) with a HTML you will need to use a Canvas. Example here: http://www.youtube.com/watch?v=QJa7tWScXS4
Use PaintbrushJS:
http://mezzoblue.github.com/PaintbrushJS/demo/
It lets you apply any number of filters including greyscale and works in most modern browsers. It's not a 'HTML' solution, that would not be possible without two images.
At least it works on most browsers, including ones which don't yet support HTML5.
Related
When upscaling a texture in canvas, the behavior differs greatly across browsers.
Here is a fiddle creating a 2x1 texture (composed of one black and one white texel) and applying it on a 256x128px rectangle: https://jsfiddle.net/ozirus/x1c3m50o/
var texture = ...
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 512;
ctx.canvas.height = 512;
document.body.appendChild(ctx.canvas);
ctx.setTransform(128, 0, 0, 128, 0, 0);
ctx.fillStyle = ctx.createPattern(texture, "no-repeat");;
ctx.rect(0, 0, 2, 1);
ctx.fill("evenodd");
The following screenshots showcase the results when run in different browsers.
In Chrome:
In Firefox:
In IE11/Edge:
Are there any flags/options available to control this behavior and make it more consistent?
The IE/Edge behavior where the last pixels are the result of a wrapping/repeating interpolation is the major issue I'm actually trying to solve.
PS: I am aware of some workarounds that could do the trick (half texel offset + scale up, transparent border on textures, ...) but I'd rather configure this behavior if possible.
EDIT: as pointed out by #Ryan in the comments, CanvasRenderingContext2D.imageSmoothingEnabled property can be used to have matching behaviors in nearest neighbour mode but I want to keep smooth interpolation of the textures
No.
No there is no way to access the code behind the 2D API (CPU and GPU) from javascript. The standard has many holes and thus browser developers are left to interpret it however it suits them.
If you use "repeat" you will get the same result as "no-repeat" on Edge on all 3 browsers.
On Edge, Firefox and Chrome with
ctx.fillStyle = ctx.createPattern(textureCanvas, "repeat");;
If you want the result to be like FF then use ctx.drawImage to render the bitmap. If you want irregular shapes then render using the ctx.drawImage then mask with a path and ctx.globalCompositeOperation = "destination-in". If you need to render to existing canvas content then use a second canvas to render the bitmaps and masks and then render that canvas onto the existing content.
On Edge, Firefox and Chrome with
ctx.drawImage(textureCanvas,0,1,2,1);
If you want the same result as Chrome on all 3 browsers you need to add a one pixel transparent border around the image and then render the 2 pixel center.
Same result on Firefox, Edge and chrome in example below (copied and modified from your fiddle https://jsfiddle.net/ozirus/x1c3m50o/)
var textureCanvas = document.createElement("canvas");
textureCanvas.width = 4;
textureCanvas.height = 3;
var textureContext = textureCanvas.getContext("2d");
var imgData = textureContext.createImageData(4, 3);
var i = 20;
imgData.data[i++] = 0;
imgData.data[i++] = 0;
imgData.data[i++] = 0;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
textureContext.putImageData(imgData, 0, 0);
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 512;
ctx.canvas.height = 512;
document.body.appendChild(ctx.canvas);
ctx.setTransform(128, 0, 0, 128, 0, 0);
ctx.drawImage(textureCanvas,1,1,2,1,0,0,2,1);
html {
background-color: red;
}
Yes.
So I guess that means you can control it, but indirectly using different techniques.
I just created little pixel art for my project http://i.imgur.com/9FLj6Uk.png. And I want to use it on my site. As you can see, it's small pixel art. I want one pixel to be drawn as 2*2 pixels instead of 1*1 pixel. I would redraw the picture with using 2*2 pixels instead of one but that seems to be bad solution.
I tried to use CSS on it
img.pixel {
width: 32px;
height: 32px
}
but that doesn't work, it shows weird shades in my browser. I want to see hard pixels. Does anyone know any solution for this problem?
This is the problem when I use the CSS above
Pixel art is tough in browsers, mainly due to lack of universal browser support of "pixelated" or "crisp-edges" image rendering. It should be supported in CSS4.
Currently the CSS stack looks like this, although it looks as if Chrome 30 and Opera 16 have broken support for CSS solutions
image-rendering:optimizeSpeed;
image-rendering:-moz-crisp-edges;
image-rendering:-o-crisp-edges;
image-rendering:optimize-contrast;
image-rendering:-webkit-optimize-contrast;
-ms-interpolation-mode: nearest-neighbor;
See this answer by #Phrogz, with a test case. Also see mozilla's documentation on the subject. For universal support now, a JS solution may have to work for the time being such as seen here on the great article drawing pixels is hard:
var resize = function( img, scale ) {
// Takes an image and a scaling factor and returns the scaled image
// The original image is drawn into an offscreen canvas of the same size
// and copied, pixel by pixel into another offscreen canvas with the
// new size.
var widthScaled = img.width * scale;
var heightScaled = img.height * scale;
var orig = document.createElement('canvas');
orig.width = img.width;
orig.height = img.height;
var origCtx = orig.getContext('2d');
origCtx.drawImage(img, 0, 0);
var origPixels = origCtx.getImageData(0, 0, img.width, img.height);
var scaled = document.createElement('canvas');
scaled.width = widthScaled;
scaled.height = heightScaled;
var scaledCtx = scaled.getContext('2d');
var scaledPixels = scaledCtx.getImageData( 0, 0, widthScaled, heightScaled );
for( var y = 0; y < heightScaled; y++ ) {
for( var x = 0; x < widthScaled; x++ ) {
var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
var indexScaled = (y * widthScaled + x) * 4;
scaledPixels.data[ indexScaled ] = origPixels.data[ index ];
scaledPixels.data[ indexScaled+1 ] = origPixels.data[ index+1 ];
scaledPixels.data[ indexScaled+2 ] = origPixels.data[ index+2 ];
scaledPixels.data[ indexScaled+3 ] = origPixels.data[ index+3 ];
}
}
scaledCtx.putImageData( scaledPixels, 0, 0 );
return scaled;
}
Read the article through, the presence of retina displays and mobile safari may add additional complexity to rendering the correct size pixel art. Although with iOS7's mobile safari this may be rectified.
I suggest you to use an svg image, as that will be scalable and get you what you are looking for.
You can read more about the same from the below link.
https://developer.mozilla.org/en/docs/SVG_In_HTML_Introduction
Hope this helps.
Are you looking for this? <img src="http://i.imgur.com/9FLj6Uk.png width="32" height="32">
check fiddle
I want to draw text on a canvas in the inverse color of the background (to make sure the text is readible no matter the background color). I believe in oldskool bitblt-ing, this was an XOR operation.
How to do this?
Update: most of the newer browsers now support the blending mode "difference" which can achieve the same result.
context.globalCompositeOperation = "difference";
Updated demo.
Old answer:
One should think that the XOR mode for composition would do this, but unfortunately canvas' XOR only XORs the alpha bits.
By applying the following code we can however receive a result such as this:
You can make an extension to the canvas like this:
CanvasRenderingContext2D.prototype.fillInversedText =
function(txt, x, y) {
//code - see below
}
Now you can call it on the context as the normal fillText, but with a slight change:
ctx.fillInversedText(txt, x, y);
For this to work we do the following first - measure text. Currently we can only calculate width of text and then assume the height. This may or may not work well as fonts can be very tall and so forth. Luckily this will change in the future, but for now:
var tw = this.measureText(txt).width;
var th = parseInt(ctx.font, '10');
th = (th === 0) ? 10 : th; //assume default if no font and size is set
Next thing we need to do is to setup an off-screen canvas to draw the text we want ot invert:
var co = document.createElement('canvas');
co.width = tw;
co.height = th;
Then draw the actual text. Color does not matter as we are only interested in the alpha channel for this canvas:
var octx = co.getContext('2d');
octx.font = this.font;
octx.textBaseline = 'top';
octx.fillText(txt, 0, 0);
Then we extract the pixel buffers for the area we want to draw the inverted text as well as all the pixels for the off-screen canvas which now contains our text:
var ddata = this.getImageData(x, y, tw, th);
var sdata = octx.getImageData(0, 0, tw, th);
var dd = ddata.data; //cache for increased speed
var ds = sdata.data;
var len = ds.length;
And then we invert each pixel where alpha channel for pixel is greater than 0.
for (var i = 0; i < len; i += 4) {
if (ds[i + 3] > 0) {
dd[i] = 255 - dd[i];
dd[i + 1] = 255 - dd[i + 1];
dd[i + 2] = 255 - dd[i + 2];
}
}
Finally put back the inverted image:
this.putImageData(ddata, x, y);
This may seem as a lot of operations, but it goes pretty fast.
Demo (warning if you are sensitive to flicker)
(the psychedelic background is just to have some variations as fiddle needs external images and most are prevented by CORS when we use pixel manipulation).
I've removed my old answer, as it did not solve the question. As of recently, there are new globalCompositeOperations that do all kinds of great things. I've created an example that shows how to obtain inverted text. In case that link breaks, the method is essentially this:
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "white";
//draw inverted things here
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
I need an image that is drawn in html5 canvas to look like a gif image (make it have 256 colors only). Is there any function that makes canvas have only 256 or less colors and make it convert images that are placed into it have only 256 or less colors?
Thanks in advance
I'm not sure there is such a method in the 2D context of the canvas.
However, it does not seem to be very complicate. 256 colors = 8 levels of red, green and blue.
So you can convert the image to respect this rule.
To achieve this, you need to change the color of each pixels.
Whad I'd do is to create a temporary canvas to manipulate the image, something like this (not tested):
//create copy of the image in a temporary context
this.tmpCanvas = document.createElement('canvas');
this.tmpCanvas.width = myIMage.width;
this.tmpCanvas.height = myIMage.height;
var scaledContext = this.tmpCanvas.getContext('2d');
scaledContext.drawImage(myIMage, 0, 0, myIMage.width, myIMage.height, 0, 0, myIMage.width, myIMage.height);
//get image data
imageData = scaledContext.getImageData(0, 0, myIMage.width, myIMage.height);
//loop over copied image date and modifiy pixels value
for(var i = 0; i < imageData.length; i += 4) { // 4 because RGBA
//red component
imageData[i] = Math.round(imageData[i] / 64);
//green component
imageData[i + 1] = Math.round(imageData[i+1] / 64);
//blue component
imageData[i + 2] = Math.round(imageData[i+2] / 64);
}
scaledContext.putImageData(imageData, 0, 0);
I didn't test this code, but the idea is there
I want to insert a pixel with a color, and I use this code:
context.fillStyle='RGB('+s[i]+')';
context.fillRect(i,y,1,1)
Is there a shorter way to do it? e.g. in a single line of code?
My main goal is to reduce the amount of code.
There really isn't a shorter way to do it besides the method you used above. You don't have to include a fillStyle every time so it essentially is only one line of code to fill a pixel.
Like Petteri pointed out there is another way to fill pixels, it involves manipulating the pixel data directly.
Live Demo
var canvasData = ctx.getImageData(0,0,canvasWidth,canvasHeight);
//color 100,100 red
canvasData.data[((100*(canvasData.width*4)) + (100*4)) + 0] = 255;
ctx.putImageData(canvasData,0,0);
also note with the above method you would need to repeat that line 3 times once for each component of the color. For example to set red, green, blue, and the alpha you would use
canvasData.data[((100*(canvasData.width*4)) + (100*4)) + 0] //red
canvasData.data[((100*(canvasData.width*4)) + (100*4)) + 1] //green
canvasData.data[((100*(canvasData.width*4)) + (100*4)) + 2] //blue
canvasData.data[((100*(canvasData.width*4)) + (100*4)) + 3] //alpha
granted you could have your data in an array, and just loop through that and color as needed.
You can edit the image data of the canvas directly. Here is a good example how to do it: http://beej.us/blog/2010/02/html5s-canvas-part-ii-pixel-manipulation/
No, there is no single line of code way to change a single pixel to one color. Well, there sort-of is.
As Petteri noted, there is a way to change each pixel directly, which will probably accomplish what you want. I assume what you want is to change one pixel to one color, and the next pixel to another color, etc.
For instance here is a function for desaturating a canvas. What it does is takes every pixel and averages the RGB values to be color-neutral (have no saturation). The result is a grayscale image.
function grayscale() {
var imageData = ctx.getImageData(0,0,can.width, can.height);
var pixels = imageData.data;
var numPixels = pixels.length;
ctx.clearRect(0, 0, can.width, can.height);
for (var i = 0; i < numPixels; i++) {
var average = (pixels[i*4] + pixels[i*4+1] + pixels[i*4+2]) /3;
// set red green and blue pixels to the average value
pixels[i*4] = average;
pixels[i*4+1] = average;
pixels[i*4+2] = average;
}
ctx.putImageData(imageData, 0, 0);
}
As you can see it is iterating over each pixel. It could be easily mofied to have each pixel changed as a one-liner.
Instead of:
pixels[i*4] = average;
pixels[i*4+1] = average;
pixels[i*4+2] = average;
You'd write:
// Take out 3 values starting at i*4 and add the new RGB for that pixel
pixels.splice(i*4,3,REDVALUE,GREENVALUE,BLUEVALUE);
Which would accomplish what you'd want. It's not the most efficient way under the sun, but it would accomplish your goal :)
fillStyle="rgb("+a[m];
fillRect(m,o,1,1);
Someone did with that :P