我已经就视网膜显示屏上的画布模糊问题阅读了多项建议(例如使用window.devicePixelRatio方法; here,here以及here)但我还没有能够将建议的解决方案应用于我的具体问题。下面的脚本首先创建一个带有一些随机图像数据(看起来模糊)的画布,然后将图像导出到SVG元素并重新缩放它(当然仍然模糊)。我在2016年末使用MBP触摸吧和野生动物园。有关如何避免模糊和实现清晰边缘的任何建议?请记住,初始imageData应具有固定的宽度和高度。
<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<body></body>
<script type="text/javascript">
var width = 100;
var height = 100;
var canvas = d3.select("body").append("canvas");
context = canvas.node().getContext("2d"),
canvas
.attr("width", width)
.attr("height", height)
.style("width", width + "px")
.style("height", height + "px")
//this is the part that should normally take care of blurriness
if (window.devicePixelRatio > 1) {
var devicePixelRatio = window.devicePixelRatio || 1;
var backingStoreRatio = context.webkitBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
var ratio = devicePixelRatio / backingStoreRatio;
canvas
.attr('width', width * ratio)
.attr('height', height * ratio)
.style('width', width + 'px')
.style('height', height + 'px');
context.scale(ratio, ratio);
}
var imageData = context.createImageData(width, height);
for (var i = 0, l = 0; i<height; ++i) {
for (j = 0; j<width; ++j, l += 4) {
imageData.data[l+0] = Math.round( Math.random() * 255);
imageData.data[l+1] = Math.round( Math.random() * 255);
imageData.data[l+2] = Math.round( Math.random() * 255);
imageData.data[l+3] = Math.round( Math.random() * 255);
}
}
context.putImageData(imageData, 0, 0);
var ImageD = canvas.node().toDataURL("img/png");
var svg = d3.select('body').append('svg').attr('width', width*5).attr('height', height*5);
svg.append("svg:image").datum(ImageD).attr("xlink:href", function(d) {return d})
.attr("height", height*5).attr("width", width*5)
</script>
答案 0 :(得分:1)
如果您使用上下文进行渲染,那么您可以使用的代码,但是您将像素直接写入图像缓冲区。这不受2D上下文转换的影响,因此您的代码不会扩展。
在你的代码行
context.scale(ratio, ratio);
缩放画布渲染的不适用于imagedata。
如果您知道该设备是视网膜,那么这是一个简单的修复。它将画布分辨率加倍,然后设置随机像素。为了保持原始代码,我将2 x 2像素设置为相同的随机值。模糊将消失,但随机模式保持不变。
const width = 100;
const height = 100;
const w = width; // because I hate cluttered code
const h = height;
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");
canvas.width = w * 2;
canvas.height = h * 2;
canvas.style.width = w + "px";
canvas.style.height = h + "px";
const imageData = ctx.createImageData(w * 2, h * 2);
// get 32bit view of data
const b32 = new Uint32Array(imageData.data.buffer);
// this is the part that you need to change as the canvas resolution is double
for (let i = 0, l = 0; i< h; i ++) {
for (let j = 0; j < w; j ++) {
const idx = i * w* 2 + j * 2;
b32[idx + w + 1] = b32[idx + w] = b32[idx + 1] = b32[idx] = (Math.random() * 0xFFFFFFFF) | 0;
}
}
ctx.putImageData(imageData, 0, 0);
const ImageD = canvas.toDataURL("img/png");
const svg = d3.select('body').append('svg').attr('width', width*5).attr('height', height*5);
svg.append("svg:image").datum(ImageD).attr("xlink:href", function(d) {return d})
.attr("height", height*5).attr("width", width*5)
答案 1 :(得分:1)
我终于找到了解决方案。我使用以下组合:window.devicePixelRatio用于获取视网膜像素比率,从here获取屏幕外画布,然后放大从here
获取的上下文<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<body></body>
<script type="text/javascript">
const width = 20;
const height = 20;
const scale = 10; // the higher the number the crisper the custom image
var canvas = d3.select("body").append("canvas");
context = canvas.node().getContext("2d");
const ratio = window.devicePixelRatio || 1;
canvas.attr('width', width * ratio * scale)
.attr('height', height * ratio * scale)
.style('width', width * scale + 'px')
.style('height', height * scale + 'px');
var imageData = context.createImageData(width, height);
for (var i = 0, l = 0; i<height; ++i) {
for (j = 0; j<width; ++j, l += 4) {
imageData.data[l+0] = Math.round( Math.random() * 255);
imageData.data[l+1] = Math.round( Math.random() * 255);
imageData.data[l+2] = Math.round( Math.random() * 255);
imageData.data[l+3] = Math.round( Math.random() * 255);
}
}
const offCtx = canvas.node().cloneNode().getContext('2d'); // create an off screen canvas
offCtx.putImageData(imageData, 0,0);
context.scale(ratio * scale, ratio * scale);
context.mozImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;
context.drawImage(offCtx.canvas, 0,0);
//export image
var ImageD = canvas.node().toDataURL("img/png");
//load image
d3.select('body').append('svg').attr("height", 500).attr("width", 500).append("svg:image").datum(ImageD).attr("xlink:href", function(d) {return d})
.attr("height", 500).attr("width", 500);
</script>