简体   繁体   English

如何提高Html5画布的路径质量?

[英]How can i improve my Html5 Canvas pathing Quality?

I have been writing a little javascript plugin, and i am having a little trouble with improving the canvas overall quality of the render. 我一直在写一个小javascript插件,并且在改善渲染的画布整体质量方面遇到了一些麻烦。 I have searched over the web here and there but can not find anything that makes sense. 我已经在网上搜索过,但是找不到任何有意义的信息。

The lines created from my curves are NOT smooth, if you look at the jsfiddle below you will understand what I mean. 从我的曲线创建的线并不平滑,如果您看下面的jsfiddle,您将会理解我的意思。 It kind of looks pixelated. 看起来有点像素化。 Is there a way to improve the quality? 有没有提高质量的方法? Or is there a Canvas Framework that already uses some method to auto improve its quality that I can use in my project? 还是有一个Canvas框架已经使用某种方法来自动提高我可以在项目中使用的质量?

My Canvas Render 我的画布渲染

Not sure if this helps but i am using this code at the start of my script: 不确定是否有帮助,但是我在脚本的开头使用了以下代码:

var c = document.getElementsByClassName("canvas");

  for (i = 0; i < c.length; i++) {
    var canvas = c[i];
    var ctx = canvas.getContext("2d");
    ctx.clearRect(0,0, canvas.width, canvas.height);
    ctx.lineWidth=1;
  }

}

Thanks in advance 提前致谢

Example of my Curve Code: 我的曲线代码示例:

var data = {
diameter: 250,
slant: 20,
height: 290
};

for (i = 0; i < c.length; i++) {
  var canvas = c[i];
  var ctx = canvas.getContext("2d");
  ctx.beginPath();
    ctx.moveTo( 150 + ((data.diameter / 2) + data.slant ), (data.height - 3) );
    ctx.quadraticCurveTo( 150 , (data.height - 15), 150 - ((data.diameter / 2) + data.slant ), (data.height - 3));
    ctx.lineTo( 150 - ((data.diameter / 2) + data.slant ), data.height );
    ctx.quadraticCurveTo( 150 , (data.height + 5), 150 + ((data.diameter / 2) + data.slant ), data.height);
  ctx.closePath();
  ctx.stroke();
}

The Problem 问题

This is one of those cases where it's almost impossible to get a smooth result without manually tweaking it. 这是在不手动调整的情况下几乎不可能获得平滑结果的情况之一。

The cause has to do with the minimal space to distribute smoothing pixels. 原因与分配平滑像素的最小空间有关。 In this case we only have a single pixel height between each section in the quadratic curve. 在这种情况下,我们在二次曲线的每个部分之间只有一个像素高度。

If we look at a curve with no smoothing we can more clearly see this limitation (without smoothing each pixel sits on an integer position): 如果我们看一下没有平滑的曲线,我们可以更清楚地看到此限制(不进行平滑,每个像素都位于整数位置):

没有平滑

The red line indicates a single section and we can see that the transition between the previous and next section has to be distributed over the height one pixel. 红线表示单个部分,我们可以看到上一部分和下一部分之间的过渡必须分布在一个像素的高度上。 See my answer here for how this works. 在这里查看我的答案以了解其工作原理。

Smoothing is based on the remaining fraction for the point's coordinate conversion to integer. 平滑基于将点的坐标转换为整数的剩余分数。 Since smoothing then uses this fraction to determine the color and alpha based on stroke main color and background color to add a shaded pixel, we will quickly run into limitations as each pixel used for smoothing occupies a whole pixel itself and due to the lack of space as here, the shades will be very rough and therefor revealing. 由于平滑然后使用此分数来确定颜色和基于笔触主色和背景色的Alpha值来添加阴影像素,因此我们很快就会遇到限制,因为用于平滑的每个像素本身都占据了整个像素,并且由于缺少空间像这里一样,阴影会非常粗糙,因此很明显。

When a long line goes from y to y+/-1 (or x to x+/-1) there is not a single pixel between the end points that would land on a perfect bound which means every pixel between is instead a shade. 当一条长线从y到y +/- 1(或从x到x +/- 1)移动时,端点之间没有单个像素会落在理想边界上,这意味着它们之间的每个像素都是阴影。

If we take a closer look at a couple of segments from the current line we can see the shades more clearly and how it affects the result : 如果我们仔细看一下当前行中的几个线段,我们可以更清楚地看到阴影及其对结果的影响:

特写

Additionally 另外

Though this explains the principle in general - Other problems are (as I barely hinted about in revision 1 (last paragraph) of this answer a few days ago, but removed and forgot about going deeper into it) is that lines drawn on top of each other in general, will contribute to contrast as the alpha pixels will blend and in some parts introduce higher contrast. 尽管这从总体上解释了该原理-其他问题(正如我几天前在此答案的修订版1 (最后一段)中几乎没有暗示过,但已删除并忘了深入探讨)是在每个问题上方绘制了线条通常,其他像素将有助于对比度,因为alpha像素将融合并且在某些部分引入更高的对比度。

You will have to go over the code to remove unneeded strokes so you get a single stroke in each location. 您将不得不检查代码以删除不需要的笔画,以便在每个位置都得到一个笔画。 You have for instance some closePaths() that will connect end of path with the beginning and draw double lines and so forth. 例如,您有一些closePaths()会将路径的末尾与起点连接起来,并绘制双线,依此类推。

A combination of these two should give a nice balance between smooth and sharp. 两者的结合应该在平滑和锐利之间达到良好的平衡。

Smoothing test-bench 平滑测试台

This demo allows you to see the effect for how smoothing is distributed based on available space. 该演示使您可以查看如何根据可用空间分配平滑效果。

The more bent the curve is, the shorter each section becomes and would require less smoothing. 曲线弯曲得越多,每个部分变得越短,并且所需的平滑度也就越小。 The result: smoother line. 结果:线条更平滑。

 var ctx = c.getContext("2d"); ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = false; // for zoom! function render() { ctx.clearRect(0, 0, c.width, c.height); !!t.checked ? ctx.setTransform(1,0,0,1,0.5,0.5):ctx.setTransform(1,0,0,1,0,0); ctx.beginPath(); ctx.moveTo(0,1); ctx.quadraticCurveTo(150, +v.value, 300, 1); ctx.lineWidth = +lw.value; ctx.strokeStyle = "hsl(0,0%," + l.value + "%)"; ctx.stroke(); vv.innerHTML = v.value; lv.innerHTML = l.value; lwv.innerHTML = lw.value; ctx.drawImage(c, 0, 0, 300, 300, 304, 0, 1200, 1200); // zoom } render(); v.oninput=v.onchange=l.oninput=l.onchange=t.onchange=lw.oninput=render; 
 html, body {margin:0;font:12px sans-serif}; #c {margin-top:5px} 
 <label>Bend: <input id=v type=range min=1 max=290 value=1></label> <span id=vv></span><br> <label>Lightness: <input id=l type=range min=0 max=60 value=0></label> <span id=lv></span><br> <label>Translate 1/2 pixel: <input id=t type=checkbox></label><br> <label>Line width: <input id=lw type=range min=0.25 max=2 step=0.25 value=1></label> <span id=lwv></span><br> <canvas id=c width=580></canvas> 

Solution

There is no good solution unless the resolution could have been increased. 除非可以提高分辨率,否则没有好的解决方案。 So we are stuck with tweaking the colors and geometry to give a more smooth result. 因此,我们不得不调整颜色和几何形状以提供更平滑的结果。

We can use a few of tricks to get around: 我们可以使用一些技巧来解决:

  1. We can reduce the line width to 0.5 - 0.75 so we get a less visible color gradient used for shading. 我们可以将线宽减小到0.5-0.75,这样就可以减少用于着色的可见颜色渐变。
  2. We can dim the color to decrease the contrast 我们可以调暗颜色以降低对比度
  3. We can translate half pixel. 我们可以翻译一半像素。 This will work in some cases, others not. 在某些情况下这会起作用,而在其他情况下则不会。
  4. If sharpness is not essential, increasing the line width instead may help balancing out shades. 如果清晰度不是必不可少的,则增加线宽可以帮助平衡阴影。 Example value could be 1.5 combined with a lighter color/gray. 示例值可以是1.5,同时具有较浅的颜色/灰色。
  5. We could use shadow too but this is an performance hungry approach as it uses more memory as well as Gaussian blur, together with two extra composition steps and is relatively slow. 我们也可以使用阴影,但这是一种性能饥饿的方法,因为它使用更多的内存以及高斯模糊,再加上两个额外的合成步骤,而且速度相对较慢。 I would recommend using 4) instead. 我建议使用4)代替。

1) and 2) are somewhat related as using a line width < 1 will force sub-pixeling on the whole line which means no pixel is pure black. 1)和2)在某种程度上相关,因为使用小于1的线宽会在整个线上强制子像素化,这意味着没有像素是纯黑色的。 The goal of both techniques is to reduce the contrast to camouflage the shade gradients giving the illusion of being a sharper/thinner line. 两种技术的目的都是为了降低对比度,以伪装阴影渐变,从而给人以更清晰更细的线条的感觉。

Note that 3) will only improve pixels that as a result lands on a exact pixel bound. 请注意,3)仅会改善像素,从而使像素落在精确的像素边界上。 All other cases will still be blurry. 所有其他情况仍将是模糊的。 In this case this trick will have little to no effect on the curve, but serves well for the rectangles and vertical and horizontal lines. 在这种情况下,此技巧对曲线几乎没有影响,但对矩形以及垂直和水平线效果很好。

If we apply these tricks by using the test-bench above, we'll get some usable values: 如果使用上面的测试平台应用这些技巧,我们将获得一些可用的值:

Variation 1 变体1

ctx.lineWidth = 1.25;              // gives some space for lightness
ctx.strokeStyle = "hsl(0,0%,50%)"; // reduces contrast
ctx.setTransform(1,0,0,1,0.5,0.5); // not so useful for the curve itself

结果2

Variation 2 : 变化2

ctx.lineWidth = 0.5;               // sub-pixels all points
ctx.setTransform(1,0,0,1,0.5,0.5); // not so useful for the curve itself

结果1

We can fine-tune further by experimenting with line width and the stroke color/lightness. 我们可以通过试验线条宽度和笔触颜色/亮度来进一步进行微调。

An alternative is to produce a more accurate result for the curves using Photoshop or something similar which has better smoothing algorithms, and use that as image instead of using native curve. 一种替代方法是使用Photoshop或具有更好平滑算法的类似方法为曲线生成更准确的结果,并将其用作图像而不是使用本机曲线。

This question struck me as a little odd. 这个问题使我感到有些奇怪。 Canvas rendering, though not the best when compared to high end renderers is still very good. 画布渲染虽然与高端渲染器相比并不是最好的,但仍然非常好。 So why is there such a problem with this example. 那么为什么这个例子有这样的问题。 I was about to leave it, but 500 points is worth another look. 我本来打算离开的,但500点值得再次关注。 From that I can give two bits of advice, a solution, and an alternative. 由此,我可以给出两个建议,一个解决方案和一个替代方案。

First, designers and their designs must incorporate the limits of the media. 首先,设计师及其设计必须纳入媒体的限制。 It may sound a little presumptuous but you are trying to reduce the irreducible, you can not get rid of aliasing on a bitmap display. 听起来可能有些冒昧,但您正在尝试减少不可约的内容,无法摆脱位图显示上的混叠。

Second, Always write neat well commented code. 其次,始终编写简洁且注释良好的代码。 There are 4 answers here and no-one picked out the flaw. 这里有4个答案,没有人发现这个缺陷。 That is because the presented code is rather messy and hard to understand. 这是因为所提供的代码相当凌乱且难以理解。 I am guessing (almost like me) the others skipped your code altogether rather than work out what it was doing wrong. 我猜(几乎像我一样),其他人完全跳过了您的代码,而不是弄清楚它做错了什么。

Please Note 请注意

The quality of images in all the answers for this question may be scaled (thus resampled) by the browser. 浏览器可以缩放(因此重新采样)此问题的所有答案中的图像质量。 To make a true comparison it is best to view the images on a separate page so that they are not scaled. 为了进行真正的比较,最好在单独的页面上查看图像,以免缩放图像。

Results of study of problem in order of quality (in my view) 按质量顺序研究问题的结果(在我看来)

Genetic algorithm 遗传算法

The best method I found to improve the quality, a method not normally associated to computer graphics, is to use a very simple form of a genetic algorithm (GA) to search for the best solution by making subtle changes to the rendering process. 我发现提高质量的最佳方法(通常与计算机图形无关)是使用非常简单的遗传算法(GA)形式,通过对渲染过程进行细微更改来寻找最佳解决方案。

Sub pixel positioning, line width, filter selection, compositing, resampling and colour changes can make marked changes to the final result. 子像素定位,线宽,滤镜选择,合成,重采样和颜色更改可以对最终结果进行明显更改。 This present billions of possible combinations, any one of which could be the best. 目前存在数十亿种可能的组合,其中任何一种都可能是最好的。 GAs are well suited to finding solutions to these types of searches, though in this case the fitness test was problematic, because the quality is subjective the fitness test has to be also, and thus requires human input. GA非常适合为这些类型的搜索找到解决方案,尽管在这种情况下,适应性测试是有问题的,因为质量是主观的,适应性测试也必须如此,因此需要人工输入。

After many iterations and taking up rather a bit more of my time than I wanted I found a method very well suited to this type of image (many thin closely spaced lines) The GA is not suitable for public release. 经过多次迭代,并花费了我很多时间,比我想要的更多,我发现一种非常适合此类图像的方法(许多细间距行),GA不适合公开发布。 Fortunately we are only interested in the fittest solution and the GA created a sequence of steps that are repeatable and consistent for the particular style it was run to solve. 幸运的是,我们只对最适合的解决方案感兴趣,并且GA创建了一系列步骤,这些步骤对于要解决的特定样式可以重复且一致。

The result of the GA search is. GA搜索的结果是。

最好的结果

see Note 1 for processing steps 有关处理步骤,请参见注释1

The results is far better than I expected so I had to have a closed look and noticed 2 distinct features that set this image apart from all the others presented in this and other answers. 结果远远好于我的预期,所以我不得不闭上眼睛,注意到2个独特的功能,使该图像与本答案和其他答案中呈现的所有其他图像区分开。

  • Anti-aliasing is non uniform. 抗锯齿是不统一的。 Where you would normally expect a uniform change in intensity this method produces a stepped gradient (why this makes it look better I do not know) 通常您期望强度发生均匀变化的地方,此方法会产生阶梯式渐变(为什么这样看起来更好,我不知道)
  • Dark nodes. 黑暗节点。 Just where the transition from one row to the next is almost complete the line below or above is rendered noticeably darker for a few pixels then reverts back to the lighter shade when the line is fitting the row. 从一行到下一行的过渡几乎完成的地方,下面或上面的线会明显变暗几个像素,然后在该行适合该行时恢复为较亮的阴影。 This seams to compensate lightening of the overall line as it shares its intencity across two rows. 接缝可弥补整体线的亮度降低,因为它在两行之间共享强度。

在此处输入图片说明

This has given me some food for thought and I will see if these features can be incorporated directly into the line and curve scan line rendering. 这给了我一些思考的机会,我将看看是否可以将这些功能直接合并到线和曲线扫描线渲染中。

Note 1 The methods used to render the above image. 注意1用于渲染以上图像的方法。 Off screen canvas size 1200 by 1200. Rendered at scale 4 ctx.setTransform(4,0,0,4,0,0) , pixel y offset 3 ctx.translate(0,3) , Line width 0.9pixels rendered twice on white background, 1 pixel photon count blur repeated 3 times (similar to convolution 3*3 gaussian blur but a little less weight along the diagonals), 4 times down samples via 2 step downsample using photon count means (each pixel channel is the square root of the mean of the squares of the 4 (2 by 2) sampled pixels). 屏幕外画布大小1200 ctx.setTransform(4,0,0,4,0,0) 。以4 ctx.setTransform(4,0,0,4,0,0)缩放,像素y偏移3 ctx.translate(0,3) ,线宽0.9pixels在白色上渲染两次背景,重复1次1像素光子计数模糊3次(类似于卷积3 * 3高斯模糊,但沿对角线的权重较小),使用光子计数方法通过2步向下采样4次向下采样(每个像素通道是像素的平方根) 4个(2 x 2)采样像素的平方的均值)。 Sharpen one pass (unfortunately that is a very complex custom sharpen filter (like some pin/pixel sharpen filters)) and then layered once with ctx.globalCompositeOperation = "multiply" and ctx.globalAlpha = 0.433 then captured on canvas. 锐化一遍(不幸的是,这是一个非常复杂的自定义锐化滤镜(如某些图钉/像素锐化滤镜)),然后使用ctx.globalCompositeOperation = "multiply"ctx.globalAlpha = 0.433进行分层,然后捕获在画布上。 All processing done on Firefox 在Firefox上完成的所有处理

Code fix 代码修复

The awful rendering result was actually caused by some minor rendering inconsistencies in you code. 可怕的渲染结果实际上是由于代码中的一些微小的渲染不一致引起的。

Below is the before and after the code fix. 下面是代码修复之前和之后。 As you can see there is a marked improvement. 如您所见,有了明显的改进。

在代码修复之前和之后。

So what did you do wrong? 那你做错了什么?

Not to much, the problem is that you where rendering lines over the top of existing lines. 问题不多,就是您在现有行上方绘制了行。 This has the effect of increasing the contrast of those lines, the render does not know you don't want the existing colours and thus adds to the existing anti aliasing doubling the opacity and destroying the effect. 这具有增加这些线条的对比度的效果,渲染器不知道您不想要现有的颜色,因此会增加现有的抗锯齿效果,从而使不透明度加倍,并破坏效果。

Bellow your code with only the rendering of the shape. 仅用形状的渲染来折叠您的代码。 Comments show the changes. 注释显示更改。

ctx.beginPath();
// removed the top and bottom lines of the rectangle
ctx.moveTo(150, 0);
ctx.lineTo(150, 75);
ctx.moveTo(153, 0);
ctx.lineTo(153, 75);
// dont need close path
ctx.stroke();
ctx.beginPath();
ctx.moveTo((150 - (data.diameter / 2)), 80);
ctx.quadraticCurveTo(150, 70, 150 + (data.diameter / 2), 80);
ctx.lineTo(150 + (data.diameter / 2), 83);
ctx.quadraticCurveTo(150, 73, 150 - (data.diameter / 2), 83);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
// removed the two quadratic curves that where drawing over the top of existing ones
ctx.moveTo(150 + (data.diameter / 2), 83);
ctx.lineTo(150 + ((data.diameter / 2) + data.slant), data.height);
ctx.moveTo(150 - ((data.diameter / 2) + data.slant), data.height);
ctx.lineTo(150 - (data.diameter / 2), 83);
// dont need close path
ctx.stroke();

ctx.beginPath();
// removed a curve
ctx.moveTo(150 + ((data.diameter / 2) + data.slant), (data.height - 3));
ctx.quadraticCurveTo(150, (data.height - 15), 150 - ((data.diameter / 2) + data.slant), (data.height - 3));
// dont need close path
ctx.stroke();

ctx.beginPath();
ctx.moveTo(150 + ((data.diameter / 2) + data.slant), data.height);
ctx.quadraticCurveTo(150, (data.height - 10), 150 - ((data.diameter / 2) + data.slant), data.height);
ctx.quadraticCurveTo(150, (data.height + 5), 150 + ((data.diameter / 2) + data.slant), data.height);
ctx.closePath();
ctx.stroke();

So now the render is much better. 因此,现在渲染效果更好。

Subjective eye 主观的眼睛

The code fix in my opinion is the best solution that can be achieved with the minimum of effort. 我认为代码修复是可以用最少的精力实现的最佳解决方案。 As quality is subjective below I present several more methods that may or may not improve the quality, dependent on the eye of the judge. 由于质量在下面是主观的,因此我提出了几种可能会也可能不会提高质量的方法,具体取决于法官的判断。

DOWN SAMPLING 下采样

Another why of improving render quality is to down sample.This involves simply rendering the image at a higher resolution and then re rendering the image at a lower resolution. 改善渲染质量的另一个原因是降低采样率,这涉及简单地以较高的分辨率渲染图像,然后以较低的分辨率重新渲染图像。 Each pixel is then an average of 2 or more pixels from the original. 那么每个像素是原始像素的平均2个或更多像素。

There are many down sampling methods, but many are not of any practical use due to the time they take to process the image. 下采样方法很多,但是由于处理图像需要花费时间,因此许多方法没有实际用途。

The quickest down sampling can be done via the GPU and native canvas render calls. 最快的向下采样可以通过GPU和本地画布渲染调用来完成。 Simply create an offscreen canvas at a resolution 2 time or 4 time greater than required, then use the transform to scale the image rendering up (so you don't need to change the rendering code). 只需以比所需的分辨率大2倍或4倍的分辨率创建一个屏幕外画布,然后使用该变换按比例放大图像渲染(因此您无需更改渲染代码)。 Then you render that image at the required resolution for the result. 然后,以所需的分辨率渲染该图像。

Example of downsampling using 2D API and JS 使用2D API和JS进行下采样的示例

var canvas = document.getElementById("myCanvas"); // get onscreen canvas
// set up the up scales offscreen canvas
var display = {};
display.width = canvas.width;
display.height = canvas.height;
var downSampleSize = 2;
var canvasUp = document.createElement("canvas");
canvasUp.width = display.width * downSampleSize;
canvasUp.height = display.height * downSampleSize;
var ctx = canvasUp.getContext("2D");
ctx.setTransform(downSampleSize,0,0,downSampleSize,0,0);

// call the render function and render to the offscreen canvas

Once you have the image just render it to you onscreen canvas

ctx = canvas.getContext("2d");
ctx.drawImage(canvasUp,0,0,canvas.width,canvas.height);

The following images shows the result of 4* down sampling and varying the line width from 1.2 pixels down to 0.9 pixels (Note the upsampled line width is 4 * that. 4.8, 4.4, 4, & 3.6) 下图显示了4 *下采样并将线宽从1.2像素降低到0.9像素的结果(请注意,上采样的线宽是4 * 4.8、4.4、4和3.6)

在此处输入图片说明 在此处输入图片说明

Next image 4* down sample using Lanczos resampling a reasonably quick resample (better suited to pictures) 使用Lanczos对下一张图片进行4 *向下采样,并以相当快的速度重新采样(更适合于图片)

在此处输入图片说明

Down sampling is quick and requires very little modification to the original code to work. 下采样速度快,并且只需很少修改原始代码即可工作。 The resulting image will improve the look of fine detail and create a slightly better antialiased line. 生成的图像将改善精细细节的外观,并创建稍微更好的抗锯齿线条。

Down sampling also allows for much finer control of the (apparent) line width. 下采样还可以更好地控制(视在)线宽。 rendering at display resolution gives poor results under 1/4 pixel changes in line width. 在显示分辨率下进行渲染时,如果线宽变化1/4像素,则效果不佳。 Using downsampling you double and quadruple that 1/8th and 1/16th fine detail (keep in mind there are other types of aliasing effect that come into play when rendering at sub pixels resolutions) 使用下采样可使1/8和1/16精细细节翻倍和翻倍(请注意,在以亚像​​素分辨率渲染时,还有其他类型的混叠效果会起作用)

Dynamic Range 动态范围

Dynamic range in digital media refers to the range of values that the media can handle. 数字媒体中的动态范围是指媒体可以处理的值的范围。 For the canvas that range is 256 (8bits) per color channel. 对于画布,该范围是每个颜色通道256(8位)。 The human eye has a hard time picking the difference between to concurrent values, say 128 and 129 so this range is almost ubiquitous in the realm of computer graphics. 人眼很难选择并发值(例如128和129)之间的差异,因此该范围在计算机图形学领域几乎无处不在。 Modern GPU though can render at much higher dynamic ranges 16bit, 24bit, 32bit per channel and even double precision floats 64bit. 尽管现代GPU可以以更高的动态范围渲染每个通道16位,24位,32位,甚至双精度浮点数为64位。

The adequate 8bit range is good for 95% of cases but suffers when the image being rendered is forced into a lower dynamic range. 适当的8位范围对于95%的情况来说是好的,但是当渲染的图像被迫进入较低的动态范围时会受到影响。 This happens when you render a line on top of a colour that is close to the line colour. 当您在接近线条颜色的颜色上方绘制线条时,会发生这种情况。 In the questio the image is rendered on not a very bright background (example #888), the result is that the anti aliasing only has a range of 7 bits halving the dynamic range. 在问题中,图像不是在非常明亮的背景上渲染(示例#888),其结果是,抗锯齿仅具有7位范围,将动态范围减半。 The problem is compounded by the fact that if the image is rendered onto a transparent background where the anti aliasing is achieved by varying the alpha channel, resulting in the introduction of a second level of artifacts. 问题在于,如果将图像渲染到透明背景上,在该透明背景上通过更改Alpha通道可实现抗锯齿,从而导致引入第二级伪像。

When you keep dynamic range in mind you can design your imagery to get the best result (within the design constraints). 当您牢记动态范围时,可以设计图像以获得最佳效果(在设计约束范围内)。 When rendering and the background is known, don't render onto a transparent canvas letting the hardware composite the final screen output, render the background onto the canvas, then render the design. 当渲染并且背景已知时,不要渲染到透明画布上,让硬件合成最终的屏幕输出,将背景渲染到画布上,然后渲染设计。 Try to keep the dynamic range as large as possible, the greater the difference in colour the better the antialiasing algorithm can deal with the intermediate colour. 尝试保持尽可能大的动态范围,颜色差异越大,抗锯齿算法可以更好地处理中间颜色。

Below is an example of rendering to various background intensities, they are rendered using 2* down sampling on pre rendered background. 以下是渲染为各种背景强度的示例,它们是在预先渲染的背景上使用2 *下采样进行渲染的。 BG denotes the background intensity . BG表示背景强度。

在渲染结果上比较背景强度。 Please note that this image is to wide too fit the page and is down sampled by the browser thus adding extra artifacts. 请注意,此图像太宽,无法容纳页面,并且已被浏览器采样,从而增加了额外的伪像。

TRUE TYPE like TRUE TYPE之类

While here there is another method. 虽然这里有另一种方法。 If you consider the screen made up of pixels, each pixel has 3 parts red, green, blue and we group them always starting at red. 如果考虑由像素组成的屏幕,则每个像素分为红色,绿色,蓝色3个部分,我们将它们始终从红色开始进行分组。

But it does not matter where a pixels starts, all that matters is that the pixel has the 3 colours rgb, it could be gbr or brg. 但像素从哪里开始并不重要,重要的是像素具有3种颜色rgb,可以是gbr或brg。 When you look at the screen like this you effectively get 3 times the horizontal resolution in regard to the edges of the pixels. 当您这样看屏幕时,相对于像素的边缘,您实际上可以获得3倍的水平分辨率。 The pixel size is still the same but offset. 像素大小仍然相同,但存在偏移。 This is how microsoft does its special font rendering (true type) Unfortunately Microsoft have many patents on the use of this method so all I can do is show you what it looks like when you render ignoring pixel boundaries. 微软就是通过这种方式来实现其特殊的字体渲染(真实类型)的。不幸的是,微软拥有使用此方法的许多专利,因此我所能做的就是向您展示在忽略像素边界时进行渲染的外观。

The effect is most pronounced in the horizontal resolution, and does not improve the vertical much (Note this is my own implementation of the canvas rendering and it's still being refined) This method also does not work for transparent images 该效果在水平分辨率中最为明显,并且在垂直方向上并没有太大改善(请注意,这是我自己的画布渲染实现,并且仍在完善中)此方法也不适用于透明图像

真实类型示例

What is a slanted line on a pixel matrix is what you should understand. 您应该了解像素矩阵上的斜线。 If you need to draw a slanted line of a single pixel width, there is no way you can prevent it from having jagged edges on it since slanting is achieved via a progressing vertical pattern of horizontal lines. 如果需要绘制单个像素宽度的倾斜线,则无法防止其上有锯齿状的边缘,因为倾斜是通过不断发展的水平线垂直模式实现的。

The solution is to have some blur effect around the lines and make the line joining smoother. 解决方案是在线条周围产生一些模糊效果,并使线条连接更平滑。

You need to use shadowColor , shadowBlur , lineCap and lineJoin properties of the canvas context to achieve this. 您需要使用canvas上下文的shadowColorshadowBlurlineCaplineJoin属性来实现此目的。

Put the following setup and try drawing you lines. 进行以下设置并尝试绘制线条。

 for (i = 0; i < c.length; i++) {
    var canvas = c[i];
    var ctx = canvas.getContext("2d");

    ctx.shadowColor = "rgba(0,0,0,1)";
    ctx.shadowBlur = 2;
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';
    ctx.lineWidth = 1;
    ctx.strokeStyle = 'black';

    ctx.clearRect(0,0, canvas.width, canvas.height);

  }

Here is the result 这是结果

结果

Try playing with the shadowColor opacity and the blur size together with the line width and color. 尝试使用shadowColor不透明度和模糊大小以及线条宽度和颜色。 You can get pretty amazing results. 您可以获得惊人的结果。

On a side note, your project sounds more SVG to me than Canvas. 附带一提,您的项目比Canvas对我来说听起来更像SVG。 Probably you should think of moving to SVG to get better drawing support and performance. 也许您应该考虑使用SVG,以获得更好的图形支持和性能。

Update 更新资料

Here is a fine adjustment 这是一个微调

ctx.shadowColor = "rgba(128,128,128,.2)";
ctx.shadowBlur = 1;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.lineWidth = 1;
ctx.strokeStyle = 'gray';

在此处输入图片说明

Sorry I'm late to the party, but all of the answers here are overcomplicating things. 抱歉,我参加聚会很晚,但是这里的所有答案都使事情变得太复杂了。

What you are actually seeing is the absence of gamma correction. 实际上看到的是没有伽玛校正。 Look at the Antialias 1&2 examples here: http://bourt.com/2014/ (you'll need to calibrate the gamma value for your monitor first), and this short explanation: https://medium.com/@alexbourt/use-gamma-everywhere-da027d9dc82f 在此处查看Antialias 1&2示例: http : //bourt.com/2014/ (您首先需要为显示器校准伽玛值),以及以下简短说明: https : //medium.com/@alexbourt/在各处使用gamma-da027d9dc82f

The vectors are drawn as if in a linear color space, while the pixels exist in a gamma-corrected space. 绘制矢量时就好像在线性颜色空间中一样,而像素存在于经过伽马校正的空间中。 It's that simple. 就这么简单。 Unfortunately, Canvas has no gamma support, so you're kind of stuck. 不幸的是,Canvas不支持伽玛,所以您有点卡住了。

There is a way to fix this, but you have to draw your stuff, then access the pixels directly and correct them for gamma yourself, like I did in those examples. 一种方法来解决这个问题,但你必须提醒你的东西,然后直接访问像素而经过修正的伽玛自己,就像我在这些例子一样。 Naturally, this is most easily done with simple graphics. 自然,使用简单的图形最容易做到这一点。 For anything more complicated you need your own rendering pipeline which takes gamma into account. 对于更复杂的事情,您需要自己的渲染管道,其中要考虑伽玛。

(Because this argument invariably comes up, I'll address it now: it's better to err on the side of gamma than not . If you say "well, I don't know what the user monitor's gamma will be", and leave it at 1.0, the result WILL BE WRONG in almost all cases. But if you take an educated guess, say 1.8, then for a substantial percentage of users you will have guessed something close to what's correct for their monitor.) (由于这个争论总是会出现,所以我现在要解决它: 最好在伽玛方面犯错,而不是不要 。如果你说“好吧,我不知道用户监视器的伽玛将是什么”,然后就把它留给它。值为1.0时,几乎在所有情况下结果都将是错误的。但是,如果您进行有根据的猜测(例如1.8),那么对于很大一部分用户,您会猜到接近于其显示器正确的内容。)

One reason for blury lines is drawing in-between pixels. 线条模糊的原因之一是在像素之间绘制。 This answer gives a good overview of the canvas coordinate system: 这个答案很好地概述了画布坐标系:

https://stackoverflow.com/a/3657831/4602079 https://stackoverflow.com/a/3657831/4602079

One way of keeping integer coordinates but still getting crisp lines is to translate the context by 0.5 pixel: 保持整数坐标但仍能获得清晰线条的一种方法是将上下文转换0.5像素:

context.translate(0.5,0.5);

Take a look at the snippet below. 看看下面的代码片段。 The second canvas is translated by (0.5, 0.5) making the line drawn with integer coordinates look crisp. 第二个画布通过(0.5,0.5)进行平移,以整数坐标绘制的线条看起来清晰。

That should get your straight lines fixed. 那应该使您的直线固定。 Curves, diagonal lines, etc. will be anti-aliased (gray pixels around the strokes). 曲线,对角线等将被消除锯齿(笔划周围的灰色像素)。 Not much you can do about it. 您对此无能为力。 The higher the resolution less visible they are and all lines except for the straight ones look better anti-aliased anyways. 分辨率越高,它们越不可见,反之,除直线之外的所有线条看起来都更好。

 function draw(ctx){ ctx.beginPath(); ctx.moveTo(25, 30); ctx.lineTo(75, 30); ctx.stroke(); ctx.beginPath(); ctx.moveTo(25, 50.5); ctx.lineTo(75, 50.5); ctx.stroke(); } draw(document.getElementById("c1").getContext("2d")) var ctx = document.getElementById("c2").getContext("2d"); ctx.translate(0.5, 0.5); draw(ctx); 
  <canvas id="c1" width="100" height="100" style="width: 100px; height: 100px"></canvas> <canvas id="c2" width="100" height="100" style="width: 100px; height: 100px"></canvas> 

Anti-aliasing helps a lot. 抗锯齿很有帮助。 But when you have angled lines that are close to horizontal, or vertical, or are gently curving, the anti-aliasing is going to be a lot more noticeable. 但是,当您有接近水平,垂直或缓和弯曲的斜线时,抗锯齿将变得更加明显。 Especially for thin lines with widths of less than a couple of a pixels or so. 特别是对于宽度小于几个像素左右的细线。

As Maciej points out, if you have a line that's around 1px width and it passed directly between two pixels, anti-aliasing will result in a line that's two pixels wide and half-grey. 正如Maciej所指出的,如果您的线宽大约为1px,并且直接在两个像素之间通过,则抗锯齿​​将导致线宽为两个像素且为灰色。

You may have to just learn to live with it. 您可能只需要学习忍受它。 There is only so much that anti-aliasing can do. 抗锯齿只能做很多事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM