I'm drawing a lot of 1px lines on an HTML5 canvas element in my code. The drawing code looks roughly like the following, the transform
variable in this case is set using d3-zoom. instructions.f32
is a Float32Array that contains the coordinates I use for drawing the lines.
context.setTransform(
transform.k,
0,
0,
transform.k,
transform.x,
transform.y
);
context.lineWidth = 1 / transform.k;
context.beginPath();
for (let i = from; i < to; ++i) {
let v1 = instructions.f32[i * 4 + 1];
let v2 = instructions.f32[i * 4 + 2];
// execute some moveTo/lineTo commands using v1 and v2 as coordinates
}
context.stroke();
One issue with this code is that the 1px lines are blurry because I'm drawing across pixel boundaries. I tried to adapt the code to snap the lines to the nearest pixels like the following:
let v1 = (Math.round(instructions.f32[i * 4 + 1] * transform.k) + 0.5) / transform.k;
let v2 = (Math.round(instructions.f32[i * 4 + 2] * transform.k) + 0.5) / transform.k;
But that still results in blurry lines like the following image (screenshot of a zoomed in image):
If I didn't have any transformation set, as far as I understand I would simply have to round the coordinates to the nearest pixel and add 0.5 to get crisp lines. But I'm not sure how to achieve this when my entire canvas is transformed, and I'm not drawing into the final coordinate system. As my attempts to correct for this have failed so far, it looks like I'm missing something here, or making some other mistake on the way.
How can I draw crisp 1px lines in canvas when transforming my entire canvas with setTransform? How exactly do I have to round the coordinates to snap the resulting lines to pixels?