Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML5, canvas, and strokeRect: some lines too narrow and blurry

A stupid simple canvas usage:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

ctx.strokeStyle = "#CCCC00";
ctx.lineWidth = 3;
ctx.strokeRect(0, 0, width, height);

Yields a rectangle with narrower lines along top and left:

enter image description here

Why does this happen? Do I need to offset with padding? It's annoying.

like image 838
Wells Avatar asked Apr 04 '12 00:04

Wells


People also ask

Why is my HTML canvas blurry?

The answer to this Question is the Pixels of the screen. The amount of blur often depends on the browser or the device you are using to view the Canvas. The Pixel ratios vary for different devices and so we get to see blurry effects.

How can I improve the quality of a picture in canvas?

Set your DPI to 300 or Higher. For accurate photo reproduction on canvas, ensure that you set your image to at least 300 DPI. If you set the DPI too low, it will result in fuzzed-out, poor-quality images. This can be straightforwardly easy when working with a porous-stock like canvas.

Is html5 canvas fast?

On the one hand, canvas was fast enough on simple functions like pencil drawing due to native implementation of basic drawing methods. On the other hand, when we implemented classic Flood Fill algorithm using Pixel Manipulation API we found that it is too slow for that class of algorithms.


1 Answers

2 things.

First, odd lineWidths (1, 3, 5, ...) will never apply cleanly with drawn on integer pixel values. This is because X and Y refer to the space between pixels rather than their centers. So a stroke of 1 that runs from [1,1] to [1,10] spills half into the pixel on the left column of pixels and half into right. If you instead draw that line from [1.5,1] to [1.5,10] then it fills half to the left, and half to the right, filling up the whole pixel column perfectly.

Any odd number width will show this behavior, but even numbers will not because they fill a full pixel on each side looking clean.


Second, the box is obscured by the top of the canvas. When you center your 3px stroke on [0,0] it spills as far up and left as [-1.5,-1.5] which is outside of the visible range of the canvas. So it appears half as thick as it should be.


See the proof of difference here:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// original box, eclipsed by canvas bounds
ctx.strokeStyle = "#CC0000";
ctx.lineWidth = 3;
ctx.strokeRect(0, 0, 20, 20);

// moved from canvas bounds
ctx.strokeStyle = "#CC0000";
ctx.lineWidth = 3;
ctx.strokeRect(25, 25, 20, 20);

// drawn on half pixel coordinated to precent blurry lines with odd integer line widths.
ctx.strokeStyle = "#CC0000";
ctx.lineWidth = 3;
ctx.strokeRect(50.5, 50.5, 20, 20);
body { margin: 10px }
<canvas id="canvas" width="100" height="100"></canvas>

Which should render this:

three boxes

The first one is like your code. The second is moved away from the top left edge to show its uniform in width. And the third shows how to render a 3px stroke without subpixel blurring.

like image 102
Alex Wayne Avatar answered Nov 15 '22 20:11

Alex Wayne