是否可以控制 canvas 纹理高档行为?
Is it possible to control canvas texture upscale behavior?
在 canvas 中放大纹理时,不同浏览器的行为差异很大。
这里是 fiddle 创建一个 2x1 纹理(由一个黑色和一个白色纹素组成)并将其应用到 256x128 像素的矩形上:https://jsfiddle.net/ozirus/x1c3m50o/
var texture = ...
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 512;
ctx.canvas.height = 512;
document.body.appendChild(ctx.canvas);
ctx.setTransform(128, 0, 0, 128, 0, 0);
ctx.fillStyle = ctx.createPattern(texture, "no-repeat");;
ctx.rect(0, 0, 2, 1);
ctx.fill("evenodd");
以下屏幕截图显示了 运行 在不同浏览器中的结果。
在Chrome中:
在 Firefox 中:
在IE11/Edge中:
是否有任何 flags/options 可用于控制此行为并使其更加一致?
最后一个像素是 wrapping/repeating 插值结果的 IE/Edge 行为是我实际要解决的主要问题。
PS:我知道一些解决方法可以解决问题(半纹素偏移 + 放大,纹理上的透明边框,...)但如果可能的话我宁愿配置此行为。
编辑:正如@Ryan 在评论中指出的那样,CanvasRenderingContext2D.imageSmoothingEnabled
属性 可用于在最近邻模式下具有匹配行为,但我想保持纹理的平滑插值
没有
不,无法从 javascript 访问 2D API(CPU 和 GPU)背后的代码。该标准有很多漏洞,因此浏览器开发人员只能根据适合他们的方式对其进行解释。
如果您在所有 3 种浏览器上使用“重复”,您将在 Edge 上获得与“不重复”相同的结果。
在 Edge、Firefox 和 Chrome 上
ctx.fillStyle = ctx.createPattern(textureCanvas, "repeat");;
如果您希望结果类似于 FF,则使用 ctx.drawImage
渲染位图。如果您想要不规则形状,则使用 ctx.drawImage
进行渲染,然后使用路径和 ctx.globalCompositeOperation = "destination-in"
进行遮罩。如果您需要呈现现有 canvas 内容,则使用第二个 canvas 呈现位图和遮罩,然后将 canvas 呈现到现有内容上。
在 Edge、Firefox 和 Chrome 上
ctx.drawImage(textureCanvas,0,1,2,1);
如果您想在所有 3 种浏览器上获得与 Chrome 相同的结果,您需要在图像周围添加一个 1 像素的透明边框,然后渲染 2 像素的中心。
在下面的示例中,Firefox、Edge 和 chrome 上的结果相同(从您的 fiddle https://jsfiddle.net/ozirus/x1c3m50o/ 复制和修改)
var textureCanvas = document.createElement("canvas");
textureCanvas.width = 4;
textureCanvas.height = 3;
var textureContext = textureCanvas.getContext("2d");
var imgData = textureContext.createImageData(4, 3);
var i = 20;
imgData.data[i++] = 0;
imgData.data[i++] = 0;
imgData.data[i++] = 0;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
textureContext.putImageData(imgData, 0, 0);
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 512;
ctx.canvas.height = 512;
document.body.appendChild(ctx.canvas);
ctx.setTransform(128, 0, 0, 128, 0, 0);
ctx.drawImage(textureCanvas,1,1,2,1,0,0,2,1);
html {
background-color: red;
}
是的。
所以我想这意味着你可以控制它,但间接地使用不同的技术。
在 canvas 中放大纹理时,不同浏览器的行为差异很大。
这里是 fiddle 创建一个 2x1 纹理(由一个黑色和一个白色纹素组成)并将其应用到 256x128 像素的矩形上:https://jsfiddle.net/ozirus/x1c3m50o/
var texture = ...
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 512;
ctx.canvas.height = 512;
document.body.appendChild(ctx.canvas);
ctx.setTransform(128, 0, 0, 128, 0, 0);
ctx.fillStyle = ctx.createPattern(texture, "no-repeat");;
ctx.rect(0, 0, 2, 1);
ctx.fill("evenodd");
以下屏幕截图显示了 运行 在不同浏览器中的结果。
在Chrome中:
在 Firefox 中:
在IE11/Edge中:
是否有任何 flags/options 可用于控制此行为并使其更加一致?
最后一个像素是 wrapping/repeating 插值结果的 IE/Edge 行为是我实际要解决的主要问题。
PS:我知道一些解决方法可以解决问题(半纹素偏移 + 放大,纹理上的透明边框,...)但如果可能的话我宁愿配置此行为。
编辑:正如@Ryan 在评论中指出的那样,CanvasRenderingContext2D.imageSmoothingEnabled
属性 可用于在最近邻模式下具有匹配行为,但我想保持纹理的平滑插值
没有
不,无法从 javascript 访问 2D API(CPU 和 GPU)背后的代码。该标准有很多漏洞,因此浏览器开发人员只能根据适合他们的方式对其进行解释。
如果您在所有 3 种浏览器上使用“重复”,您将在 Edge 上获得与“不重复”相同的结果。
在 Edge、Firefox 和 Chrome 上
ctx.fillStyle = ctx.createPattern(textureCanvas, "repeat");;
如果您希望结果类似于 FF,则使用 ctx.drawImage
渲染位图。如果您想要不规则形状,则使用 ctx.drawImage
进行渲染,然后使用路径和 ctx.globalCompositeOperation = "destination-in"
进行遮罩。如果您需要呈现现有 canvas 内容,则使用第二个 canvas 呈现位图和遮罩,然后将 canvas 呈现到现有内容上。
在 Edge、Firefox 和 Chrome 上
ctx.drawImage(textureCanvas,0,1,2,1);
如果您想在所有 3 种浏览器上获得与 Chrome 相同的结果,您需要在图像周围添加一个 1 像素的透明边框,然后渲染 2 像素的中心。
在下面的示例中,Firefox、Edge 和 chrome 上的结果相同(从您的 fiddle https://jsfiddle.net/ozirus/x1c3m50o/ 复制和修改)
var textureCanvas = document.createElement("canvas");
textureCanvas.width = 4;
textureCanvas.height = 3;
var textureContext = textureCanvas.getContext("2d");
var imgData = textureContext.createImageData(4, 3);
var i = 20;
imgData.data[i++] = 0;
imgData.data[i++] = 0;
imgData.data[i++] = 0;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
imgData.data[i++] = 255;
textureContext.putImageData(imgData, 0, 0);
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 512;
ctx.canvas.height = 512;
document.body.appendChild(ctx.canvas);
ctx.setTransform(128, 0, 0, 128, 0, 0);
ctx.drawImage(textureCanvas,1,1,2,1,0,0,2,1);
html {
background-color: red;
}
是的。
所以我想这意味着你可以控制它,但间接地使用不同的技术。