我如何提高我的Html5 Canvas路径质量? [英] How can i improve my Html5 Canvas pathing Quality?

查看:119
本文介绍了我如何提高我的Html5 Canvas路径质量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在写一个JavaScript插件,并且在改善渲染的画布整体质量方面遇到了一些麻烦。我在网上搜索过这些东西,但找不到任何有意义的东西。

从我的曲线创建的线条不光滑,如果您看下面的jsfiddle,您会理解我的意思。它看起来像是像素化的。有没有办法提高质量?或者是否有Canvas框架已经使用某种方法来自动提高我在项目中可以使用的质量?



(最后一段)这个答案几天前,但删除,并忘记了更深入到它)是一般在彼此顶部绘制的线条将有助于对比度,因为alpha像素将混合并在某些部分引入更高的对比度。



您必须仔细阅读代码才能删除不需要的笔画,以便在每个位置获得单笔画笔。例如有一些 closePaths(),它会将路径的结尾连接到开头,然后绘制双线等。



这两者的组合应该在平滑和清晰之间提供良好的平衡。



平滑测试平台



这个演示允许你看到平滑如何基于可用空间分配的效果。



曲线越弯曲,每个部分越短,不太平滑。结果:更顺畅的行。



  var ctx = c.getContext(2d ); ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = false; //用于缩放!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> ;弯曲:<输入id = v类型=范围min = 1 max = 290 value = 1>< / label>< span id = vv>< / span>< br>< label>亮度:< ; input id = l type = range min = 0 max = 60 value = 0>< / label>< span id = lv>< / span>< br>< label>翻译1/2像素:< ;输入id = t type =复选框>< / label>< br>< label>线宽:< input id = lw type = range min = 0.25 max = 2 step = 0.25 value = 1><标签>< span id = lwv>< / span>< br>< canvas id = c width = 580>< / canvas>  



没有好的解决方案,除非可以提高分辨率。所以我们坚持调整颜色和几何图形,以获得更加平滑的结果。



我们可以使用一些技巧来解决:


  1. 我们可以将线条宽度减小到0.5 - 0.75,这样我们就可以看到用于着色的颜色渐变。

  2. 调暗颜色以降低对比度

  3. 我们可以翻译半像素。这可以在某些情况下起作用,其他情况下不起作用。

  4. 如果清晰度不是必需的,则增加线宽可能有助于平衡阴影。示例值可以是1.5与较浅的颜色/灰色组合。

  5. 我们可以 https://html.spec.whatwg.org/multipage/scripting.html#when-shadows-are-drawn =noreferrer>更多内存以及高斯模糊,以及两个额外的合成步骤并且相对较慢。我会建议使用4)来代替。


    1)和2)有点相关,因为使用线宽< 1将强制整个线上的子像素,这意味着没有像素是纯黑的。这两种技术的目标是减少伪装阴影渐变的对比度,从而给出更加清晰/更细的线条的幻觉。注意,3)只会改善像素的像素作为结果落在精确的像素边界上。所有其他案例仍然模糊。在这种情况下,这个技巧对曲线几乎没有任何影响,但对于矩形和垂直和水平线很好。



    如果我们通过使用这些技巧我们会得到一些有用的值:



      ctx.lineWidth = 0.5; //子像素所有点
    ctx.setTransform(1,0,0,1,0.5,0.5); //对于曲线本身不是很有用



    我们可以通过试验线宽和笔触颜色/亮度来进一步微调。 p>

    另一个选择是使用Photoshop或其他类似的平滑算法为曲线生成更准确的结果,并将其用作图像而不是使用原生曲线。

    I have been writing a little javascript plugin, and i am having a little trouble with improving the canvas overall quality of the render. 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. 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?

    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.

    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.

    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.

    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.

    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.
    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.
    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.

    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. 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. 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

    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
    

    Variation 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
    

    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.

    这篇关于我如何提高我的Html5 Canvas路径质量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆