为什么 HTML5 canvas svg 过滤器有损?

Why is HTML5 canvas svg filter lossy?

下面是您应该能够在浏览器中 运行 的示例代码。它应该首先加载图像。图像显示后,您可以点击它查看原始图像和带有 identitdy svg 滤镜的图像之间的差异。

可能很难直接在浏览器上看到,但您可以将结果下载为图像并在任何图像查看器中打开。结果不是很奇怪。它有类似于某种压缩伪影的斑点区域。

所以我的问题是:

  1. 应用任何 svg 过滤器是否会导致质量下降?
  2. 为什么会这样? (可能是我漏掉了,文档里没有提到)
  3. 如何实现无损 svg 滤镜?

<body>
    <canvas id='canvas' width=850 height=850></canvas>
    <svg width="0" height="0" style="position:absolute;z-index:-1;">
        <defs>
            <filter id="identity" x="0" y="0" width="100%" height="100%">
                <feComponentTransfer></feComponentTransfer>
            </filter>
        </defs>
    </svg>
</body>


<script>
    canvas = document.getElementById('canvas')
    image = new Image()
    image.src = 'https://cms-assets.tutsplus.com/uploads/users/127/posts/31341/final_image/1_10.png'
    image.onload = function() {
        let ctx = canvas.getContext('2d')
        ctx.drawImage(this, 0, 0)
        canvas.image = this
    }

    canvas.onclick = function() {
        let ctx = this.getContext('2d')
        ctx.clearRect(0, 0, this.width, this.height)
        ctx.filter = 'none'
        ctx.drawImage(this.image, 0, 0)
        ctx.globalCompositeOperation = 'difference'
        ctx.filter = 'url(#identity)'
        ctx.drawImage(this.image, 0, 0)
        // 20x the difference
        ctx.filter = 'brightness(20)'
        ctx.drawImage(canvas, 0, 0)
    }
</script>

线性 RGB V sRGB(对数)

使用线性颜色模型通常会导致颜色精度下降。始终使用 sRGB 以获得最佳效果。

默认颜色插值过滤器是线性 RGB

feComponentTransfer 的默认颜色模型是线性 RGB,可能会产生一些瑕疵。

强制滤镜使用 sRGB 使用属性 color-interpolation-filters="sRGB"

下面的示例片段显示使用 sRGB 时没有损失。当您看到图像时,单击它以查看结果。应该全黑不亏。

<body>
    <canvas id='canvas' width=850 height=850></canvas>
    <svg width="0" height="0" style="position:absolute;z-index:-1;">
        <defs>
            <filter id="identity" x="0" y="0" width="100%" height="100%">
                <feComponentTransfer color-interpolation-filters="sRGB"></feComponentTransfer>
            </filter>
        </defs>
    </svg>
</body>


<script>
    canvas = document.getElementById('canvas')
    image = new Image()
    const ctx = canvas.getContext('2d')
    image.src = 'https://cms-assets.tutsplus.com/uploads/users/127/posts/31341/final_image/1_10.png'
    image.onload = () => ctx.drawImage(image, 0, 0);

    canvas.onclick = function() {
        ctx.globalCompositeOperation = 'source-over'
        ctx.filter = 'none'
        ctx.drawImage(image, 0, 0)
        ctx.globalCompositeOperation = 'difference'
        ctx.filter = 'url(#identity)'
        ctx.drawImage(image, 0, 0)

        ctx.filter = 'brightness(20)'
        ctx.drawImage(canvas, 0, 0)
    }
</script>