I am drawing frames around some images.
This is my code for drawing the top frame:
this.frame.top.height = this.frame.top.height * this.resizeMultiplyer
const pattern = this.context.createPattern(this.frame.top, 'repeat');
console.log(this.frame.top.height);
console.log(this.frame.top.height);
this.context.fillStyle = pattern;
this.context.fillRect(0, 0, this.canvas.width, this.frame.top.height);
My target is to resize the frame width with on different cases using a resizeMultiplyer that has value between 0 & 1.
Frame is getting drawn but and resize multiplier worked for frame drawing but frame image is not getting resized. So my image is basically cut off. I attaching my output that i am getting using the above code
The output I'm getting:
Output I'm getting
Expected output:
Output Expected
Instead of making the pattern with the image, created another canvas with desired height & width and drew the image inside of it. Then created the pattern using that canvas.
Related
I am building a retro styled game, that uses pixelated images. I have not yet created these images, because I wanted to know the best way of doing things.
These images will probably be a 16 or 32 PX square, but I would like to be able to scale the images as big as I like, just without any blur/distortion.
What format should I use? And how should I import them to my canvas. as well?
EDIT#1: Fixed typo & put Q back on topic. (Thank you Spence for pointing it out)
Try "Inkscape", its free
https://inkscape.org/en/
it uses SVG format (scalar vector graphics) so you will be able to scale the images as big as you like, just without any blur/distortion.
The only way to enlarge without any blur or distortion is turn each 1 pixel into a set of 2x2, 3x3, ... pixels.
For example, a single blue pixel in the top-left of the image would become a set of 4 blue pixels at [0,0], [1,0], [0,1] & [1,1]. And the same for every other pixel on the original image. The resulting image would be twice the width & height of the original image.
Since your graphics style is pixelated images, this adjustment would preserve your pixilation while also enlarging the original image.
You can code a function that uses an in-memory html5 canvas to "resize-by-multiplying" your original images as needed. This will use canvas's ability to set the RGBA values every pixel using context.getImageData and context.putImageData.
CanvasContext2d does have an option to disable the image smoothing : imageSmoothingEnabled which is set to true by default.
According to the specs, if set to false,
The image [drawn by drawImage() method] must be rendered using
nearest-neighbor interpolation.
This algorithm is the same as the one proposed by #markE in his answer.
Unfortunately, browsers still use vendor-prefix for this attribute and it wasn't implemented in both IE9 and IE10...
var img = document.querySelector('img'),
canvas = document.querySelector('canvas'),
ctx = canvas.getContext('2d');
// draw the image
img.onload = function(){
canvas.width = img.width*50;
canvas.height = img.height*50;
// disable smoothing after we change canvas' width/height
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
}
//32x32px image taken from https://stackoverflow.com/q/31910043/3702797
img.src="http://i.stack.imgur.com/3Sp5x.png"
canvas{border:.5px solid}
<img/>
<canvas></canvas>
Scroll to see the resized image in canvas
Create large icon images to which you apply a 16x16 or 32x32 tile effect. Then when you write them to the canvas (after loading the images of course) scale them down to the size you want using
context.drawImage(img,x,y,width,height);
File sizes are unlikely to jump greatly since each tile should compress fairly easily.
I am using a jquery plugin that utilities canvas to draw up spot motion animations.(http://arena.palamago.com.ar/spotMotion/)
I know that in this instance i can use animated GIF images, but the image types i use in future will be requiring higher quality and transparency.
If you look at the jsfiddle below you will see the images are not sharp, i am on a retina display and they look even worse, the original image is 800px. Canvas is not scaling the images high enough fo some unknown reason. I am fairly new to canvas and have seen a few methods for up scaling but have had no luck in getting a better result.
I looked at canvas width and canvas style width
canvas.width = "200";
canvas.height = "200"; // allow 40 pixels for status bar on iOS
canvas.style.width = "100px";
canvas.style.height = "100px";
I also looked at css image rendering techniques
canvas { image-rendering:optimizeQuality;}
http://jsfiddle.net/TsAzP/1/
Another attempt but i just cant seem to intergrate it with this plugin.
function enhanceContext(canvas, context) {
var ratio = window.devicePixelRatio || 1,
width = canvas.width,
height = canvas.height;
if (ratio > 1) {
canvas.width = width * ratio;
canvas.height = height * ratio;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
context.scale(ratio, ratio);
}
}
I have seen some very complicated methods with people writing up-scaling algorithms, i just dont understand how to put it together. If anyone knows how to improve image quality please spare me some time.
Thank you
Problem
The cause is that the source image is too large to reduce in size in a single down-scale.
The browser typically uses bi-linear interpolation over bi-cubic interpolation when it comes to the canvas element.
Bi-linear interpolation analyses 2x2 pixels while bi-cubic uses 4x4 (in down-sampling functioning as a low-pass filter to create an average pixel). If the image size reduction is too steep there is simply not enough pixels to consider for averaging and the result will be in part "choppy" or pixelated.
Solution
To solve you can do one of the following steps:
Prepare the image(s) at a smaller size in an image editor (for example Photoshop) and scale the image to the destination size you want to use (ref. retina display).
Process the image on client before drawing it by creating an off-screen canvas and scale down the image in 2-3 steps.
The first step could be a better solution for a variety of reasons such as:
Processing of the image (for size) does not happen on client
Quality in resulting image (easier to post-process)
Bandwidth reduction (less data to transfer)
Faster processing of the image (in use) on client
Saves on battery (less processing involved)
As for the second step: There's too much code in the fiddle (TL; TR), but the principle is as follows (and it's not so complicated):
/// create two temporary canvas elements
var ocanvas = document.createElement('canvas'), /// off-screen canvas
tcanvas = document.createElement('canvas'), /// temp canvas
octx = ocanvas.getContext('2d'),
tctx = ocanvas.getContext('2d');
Then we do a first step-down scaling of the image - for this example we will do it twice which is the minimum needed. You might need a third step if the size difference is huge (you can normally calculate this by using a function of log etc., but I'll leave that out of the um, "equation" here):
/// use temp canvas (tcanvas) to scale for the first step
tcanvas.width = img.width * 0.5; /// 50% allow good result with bi-linear
tcanvas.height = img.height * 0.5;
/// draw image into canvas
tctx.drawImage(img, 0, 0, tcanvas.width, tcanvas.height);
The next step is just as simple as the above but with an absolute size:
/// set destination size
ocanvas.width = 200;
ocanvas.height = 200;
/// draw temp canvas into canvas
octx.drawImage(tcanvas, 0, 0, ocanvas.width, ocanvas.height);
You can now use ocanvas in your solution instead of img.
We use two canvases as we want to use the final ocanvas to replace img directly later at the proper size. If we used one canvas we would have to resize it in the final step which mean the canvas would be cleared.
If you do need a third step then you can reuse one of the canvases.
The added advantage here is that the browser won't need to scale anything when animating which reduces the load on the CPU/GPU.
I suggest also doing this down-scaling inside a function so that the temporary canvas references (except the one you need to use pf course, which you need to return) can be easily discarded by the browser after use (GC/memory wise).
Hope this helps!
When the canvas element is resized (via the style changing) I also want to scale the canvas' drawn image as well. I cannot just change the height/width as this causes the canvas to clear itself, so I do:
Create a temporary canvas element
Draw the current canvas' image onto that temporary canvas
Resize the current canvas
Draw the temp canvas' image back to the current canvas but scaled to the new size
This results in some blurring - very noticeable after many resizes (example: when dragging to resize). How would I do this without any blurring?
EDIT: Turning off image smoothing (context.webkitImageSmoothingEnabled = false;) does not fix the problem, it simply makes it redraw it more and more jagged until the image looks nothing like the original after a number of resizes.
Called on resize event:
var tmpCanvas = null;
//Make a temporary canvas
tmpCanvas = document.createElement( "canvas" );
//Set its size to be equal
tmpCanvas.height = originalCanvas.height;
tmpCanvas.width = originalCanvas.width;
//Draw our current canvas onto it
tmpCanvas.getContext( "2d" ).drawImage( originalCanvas, 0, 0 );
//Set new dimensions
originalCanvas.width = originalCanvas.offsetWidth;
originalCanvas.height = originalCanvas.offsetHeight;
var originalContext = originalCanvas.getContext( "2d" );
//Set background and colors
originalContext.fillStyle = "#ffffff";
originalContext.strokeStyle = "#000000";
//Set paintbrush
originalContext.lineWidth = 4;
originalContext.lineCap = "round";
//Fill background as white
originalContext.fillRect( 0, 0, originalCanvas.width, originalCanvas.height );
//We have a saved signature
if ( SignatureCanvas.hasSignature === true )
{
//Draw it back but scaled (results in blurred image)
originalContext.drawImage( tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height, 0, 0, originalCanvas.width, originalCanvas.height );
/**
* This results in a blurred image as well
//Draw it back but scaled
originalContext.scale( originalCanvas.width / tmpCanvas.width, originalCanvas.height / tmpCanvas.height );
originalContext.drawImage( tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height, 0, 0, tmpCanvas.width, tmpCanvas.height );
*/
}
Is there a way to get the strokes and "scale" all those points and redraw?
Instead of taking the rendered image from the original canvas, actually redraw the image. By that, I mean execute the same logic you executed against the original canvas, but with the points involved scaled to the new size.
If you can, think about using SVG instead. It scales well by its nature.
Edit: Another option I've thought of is to simply use a gigantic canvas to start with. Sizing down tends to look better than sizing up, especially with smoothing on.
Edit II: The original answer was irrelevant, though the comment I had made is relevant, and am now promoting it and editing it to be an answer...and the answer I had given was not all that great anyway **.
Of course if you scale up raster graphics, that is, from an image with a smaller pixel dimensions of pixels, create an image with higher pixel dimensions, you are going to get blurred images. By scaling up, you're making a low resolution picture high resolution, but without the high resolution details.
There's absolutely no way around that blurriness unless you make multiple additional assumptions about your raster image like the only gray you'd see is at an image edge, or corners can only occur at apparent inflection points where the angle between the tangents of the joined curves must be 100 degrees or less. Essentially, you'd have to give additional information so that your higher resolution image can have detail "filled in". It's not all that terribly different from reverse engineering an SVG from a raster.
So, you appear to want to emulate is scaling vector graphics, in which the only solution is to save the commands, draw a SVG, or draw to a bigger canvas like Stuart Branham suggested.
** I had originally proposed that invoking drawImage would distort the pixels even if it were not scaled, and that it would be better to work with the actual pixel data. If that's true, I can't find proof, but that's irrelevant, as he wanted his image scaled up, without blurring...which is impossible, as I just mentioned.
For a Project I want to take the content of a canvas (Called SAVE_CV) and display it in another, smaller canvas.
Some things that I am aware of so far that could be causing me problems: resizing a canvas clears its content, the JS-size of a canvas is different from the CSS-size.
I want the smaller canvas to be 500px wide and appropriately high.
function restoreTaggingCV() {
var cv = document.getElementById( 'taggingCV' );
var ctx = cv.getContext( "2d" );
var styleHeight = SAVE_CV.height * 500 / SAVE_CV.width;
ctx.drawImage(SAVE_CV, 0, 0, cv.width, cv.height);
}
This is my Code so far. Whenever I try to resize the smaller canvas appropriately it only gives me a blank canvas with nothing in it. I tried to set the size with "cv.height = X" and "cv.style.height = styleHeight + 'px'" but neither worked. Also I would love to set the width of the canvas using CSS.
Appreciate any help.
EDIT
I want the image in a picture because later I want the user to mark areas in the smaller version which I then want to use to create individual imaged from the big version. I want to visualise thise area to the user. I probably could do all this by using an image and putting divs over it or something but I just fell more comfident using a canvas since I am pritty new to HTML and CSS.
Try using the CanvasRenderingContext2d.prototype.scale method. It sets the scale factor of the canvas and renders anything in the current state with it's dimensions multiplied by the factor.
So before you use the drawImage function, you scale the context appropriately (in this case, down). For example:
context.save();
context.scale(0.5, 0.5);
context.drawImage(canvas, 0, 0);
context.restore();
This would render the canvas on the context at 0.5 times it's current size. See in this fiddle how I used it to mirror a larger canvas onto a smaller, separate one.
Canvas objects don't like to be resised. After drawing Your image simply convert it toDataURL() and set as image source. They You may resize image as you want.
$img.attr('src',canvas.toDataURL());
I'm using context-blender to apply a multiply effect on the first 192 pixels of the html background-image with a fixed color to achieve a transparency effect on the header of the page.
On the html I have 2 canvas. One for the part of the image to apply the multiply effect and one for the color.
On the javascript, after setting the color of the color-canvas and the width of both canvas to the window.innerWidth I'm getting the background image with:
imageObj.src = $('html').css('background-image').replace(/^url|[\(\)]/g, '');
Now comes the problem. I want to draw a cropped image to the image to the image-canvas so I can apply the multiply effect. I'm trying to do the following:
imageObj.onload = function(){
// getting the background-image height
var imageHeight = window.innerWidth * imageObj.height / imageObj.width;
// get the corresponding pixels of the source image that correspond to the first 192 pixels of the background-image
var croppedHeight = 192 * imageObj.height / imageHeight;
// draw the image to the canvas
imageCanvas.drawImage(imageObj, 0, 0, imageObj.width, croppedHeight, 0, 0, window.innerWidth, 192);
// apply the multiply effect
colorCanvas.blendOnto( imageCanvas, 'multiply');
}
But I'm doing something wrong getting the cropped height.
Ex: For an 1536x1152 image and a 1293x679 browser container, the value I'm getting for the source cropped height is 230 but to get the correct crop I need to use something around 296.
Edit:
I'm using background-size: cover on the css to create the background-image
Edit2:
I created a fiddle to illustrate the problem. If you uncomment the line //cHeight *= magicConstant; the cropped image looks a lot better but things stop making sense. I removed the multiply effect on the fiddler but that's not required to reproduce the problem. I also noticed that the behavior changed if I remove the second canvas from the URL.
Btw, this behavior happened with google chrome, but I think the same thing happens on safari and firefox.
OK, I've fixed it. Man was that hard! Mainly because you forgot to set the imageCanvas' canvas height. It also didn't help that the image has a white border. I spent a hell of a lot of time trying to figure out where the padding was coming from.
So to start, for the case of the fiddle, in function doBlending(), set imageCanvas.canvas.height = height;
Then the calculations in crop() need to cover 2 possibilities. Is the image being scaled for height and truncated on the left or scaled for width and truncated on the bottom? I'm not going to write both for you, but here's the one for the case where it is scaled for height:
function crop(imageObj, imageCanvas, colorCanvas) {
// Assumes bg image is scaled for hight
var scale = imageObj.height / window.innerHeight;
var targetHeight = imageCanvas.canvas.height;
var targetWidth = window.innerWidth;
imageCanvas.drawImage(imageObj,
0, 0, targetWidth * scale, targetHeight * scale,
0, 0, targetWidth, targetHeight);
}
I really have no idea where you came up with the scaling factors in your example. The image is going to be scaled by multiplying both the x and y dimensions by some scale factor. That's how you preserve the aspect ratio. The scale factor will be the larger of the one to make the height of the image match the height of the window and the one to make the width of the image match the width of the window.
I think it may not be valid for you to be using window inner dimensions here. Since cover will maintain the aspect ratio of the background image it means that both of its dimensions may not be fully displayed. So if you are trying to transform between aspect ratios to determine where to clip, you would have to account for the fact that the image may flow out of the window borders.