2

Can someone please explain why does canvas does not obey color choice and how to fix it so it draw in "true" colors? I know canvas is using 0.5 pixel coordinates, so I've tried draw using 0.5 increments, but it still draw with funky colors (using ColorCop to zoom in and check colors per pixel:

line color not true blue

Here is a little snippet that supposed to draw blue lines, 2 lines next to each other and third line 1 pixel away, the result is only first line has true blue everything else is purple or worse, including first/last pixel of first line:

const canvas = document.getElementById("line"),
      ctx = canvas.getContext("2d");

canvas.width = canvas.height = 100;
ctx.strokeStyle = 'blue';
ctx.lineCap = "square";
ctx.imageSmoothingEnabled = false;

ctx.moveTo(10.5, 40.5);
ctx.lineTo(80.5, 40.5);

ctx.moveTo(10.5, 41.5);
ctx.lineTo(80.5, 41.5);

ctx.moveTo(10.5, 43.5);
ctx.lineTo(80.5, 43.5);

ctx.stroke();
<canvas id="line"></canvas>

My monitor resolution is 2560x1440 with 100% scaling. No system/browser/software or any kind of zoom/scale is used. If I draw a pixel in MS Paint, I see one pixel on the monitor no antialiasing, no artifacts.

enter image description here

vanowm
  • 9,466
  • 2
  • 21
  • 37
  • The suggested duplicate topic is not related, in fact the accepted answer in that topic made my case even worse. My display resolution is 1440p and system scaling is set to 100% – vanowm Sep 16 '21 at 02:10
  • If the answer there made the problem worse, then it's related. We need more info to be able to help you out. Please include all that information in your question directly as an [edit]. Namely, we need the true resolution set on your OS (not some brand NNNp that can be anything), the zoom level of your web-browser, the value `devicePixelRatio` outputs and that you try with an other application than ColorCop. – Kaiido Sep 16 '21 at 02:24

2 Answers2

3

do this, your canvas will show high resolution pixels

  1. set canvas width and height to (desired width and height) * devicePixelRatio * 2

  2. reassign it using css style property to what width and height you want

  3. scale context 2, 2

    By default, one unit on the canvas is exactly one pixel. A scaling transformation modifies this behavior. For instance, a scaling factor of 0.5 results in a unit size of 0.5 pixels; shapes are thus drawn at half the normal size. Similarly, a scaling factor of 2.0 increases the unit size so that one unit becomes two pixels; shapes are thus drawn at twice the normal size.

const width = height = 100
const pixelRatio = window.devicePixelRatio * 2;
const canvas = document.getElementById('line');
canvas.width = width * pixelRatio;
canvas.height = height * pixelRatio;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
const ctx = canvas.getContext("2d");
ctx.scale(pixelRatio, pixelRatio);

ctx.strokeStyle = 'blue';
ctx.lineCap = "square";

ctx.moveTo(10.5, 40.5);
ctx.lineTo(80.5, 40.5);

ctx.moveTo(10.5, 41.5);
ctx.lineTo(80.5, 41.5);

ctx.moveTo(10.5, 43.5);
ctx.lineTo(80.5, 43.5);

ctx.stroke();
<canvas id="line"></canvas>
Manoj
  • 176
  • 4
  • 1
    Use devicePixelRatio to get the actual pixel-ratio. Not all high monitors will have a ratio of 2 (e.g most mobiles don't) and forcing "normal" monitors to paint twice the pixels they can show is just killing perfs for nothing. – Kaiido Sep 16 '21 at 01:11
  • even after using devicePixelRatio it's showing blurry pixels, that's why I multiplied devicePixelRatio with 2, getting better results. **Thank you** – Manoj Sep 16 '21 at 01:37
  • Your scale factor must be the same value as the ratio you used to set the size of your canvas. – Kaiido Sep 16 '21 at 01:46
  • @ManojKumar your method works on my monitor, thank you. It shows 100% accurate colors through each line, no color variations. But will it affect other type of monitors/OS where scaling is not 100%? (I guess it really doesn't matter, because these users already not getting true pixel-to-pixel representation of the image) – vanowm Sep 16 '21 at 02:22
  • Also, will it reduce the maximum canvas limitations? https://stackoverflow.com/questions/6081483/maximum-size-of-a-canvas-element – vanowm Sep 16 '21 at 02:41
0

You are probably on a high resolution monitor. CSS will scale your canvas and will create antialiasing.

To overcome this, you can multiply the size of your canvas by the pixel ratio of your monitor and then scale down to the expected size using CSS:

const canvas = document.getElementById("line"),
      ctx = canvas.getContext("2d"),
      dPR = window.devicePixelRatio;

// multiply by dPR
canvas.width = canvas.height = 100 * dPR;
// downscale through CSS
canvas.style.width = canvas.style.height = "100px";
ctx.scale(dPR, dPR);

ctx.strokeStyle = 'blue';
ctx.lineCap = "square";
ctx.imageSmoothingEnabled = false;

ctx.moveTo(10.5, 40.5);
ctx.lineTo(80.5, 40.5);

ctx.moveTo(10.5, 41.5);
ctx.lineTo(80.5, 41.5);

ctx.moveTo(10.5, 43.5);
ctx.lineTo(80.5, 43.5);

ctx.stroke();
<canvas id="line"></canvas>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • I'm not using any type of scaling on my system, it's 1440p monitor with 100% scaling. So suggested "answered duplicate" about retina display is not related here. And your example made actually worse... https://i.imgur.com/qM1bq3U.png – vanowm Sep 16 '21 at 02:06
  • And your OS resolution? If you're on windows there might be a default 150% applied there. – Kaiido Sep 16 '21 at 02:14
  • That's what "scaling" is...100% – vanowm Sep 16 '21 at 02:15
  • "Scaling" can be at CSS level (e.g `zoom` CSS property), at browser level (e.g ctrl + [+]) or at OS level. What does `devicePixelRatio` output in your console. What is the actual pixel resolution your OS is using? Is it a true 2560px × 1440px? – Kaiido Sep 16 '21 at 02:17