来自 <canvas> toBlob 的图像插入边缘

问题描述 投票:0回答:1

我正在尝试使用画布将 1x 图像缩放到任意大小。然而,图像的每个像素的边缘总是有一些插值。我希望提供的图像与画布的外观完全匹配。不同尺度等的组合对我来说不起作用。即使右键单击 -> 在画布上保存图像也会产生相同的图像。

我也尝试在画布上使用

toDataURL
,但得到了相同的结果。

我在配备 2 倍显示屏的 macOS 上看到了这一点。在 Safari、Chrome 和 Firefox 中结果相同。

2并排:

side-by-side with the right image having blurred lines

生成的图像:

the final (right side) image

// This function is unimportant; it is used as an example for this question.
function generateImage(width, height, digitOffset) {
  const pixelsCount = width * height
  const imageData = new ImageData(width, height)
  const digits = "0123456789"
    .repeat(30)
    .substring(digitOffset, digitOffset + pixelsCount)

  for (const [index, digit] of [...digits].entries()) {
    const colours = {
      0: [0x00, 0x12, 0x19],
      1: [0x00, 0x5f, 0x73],
      2: [0x0a, 0x93, 0x96],
      3: [0x94, 0xd2, 0xbd],
      4: [0xe9, 0xd8, 0xa6],
      5: [0xee, 0x9b, 0x00],
      6: [0xca, 0x67, 0x02],
      7: [0xbb, 0x3e, 0x03],
      8: [0xae, 0x20, 0x12],
      9: [0x9b, 0x22, 0x26],
    }
    const dataOffset = index * 4

    const color = colours[digit]
    imageData.data[dataOffset] = color[0]
    imageData.data[dataOffset + 1] = color[1]
    imageData.data[dataOffset + 2] = color[2]
    imageData.data[dataOffset + 3] = 0xff
  }

  return imageData
}

/** The width/height of the 1x image. */
const size = 16
const imageData = generateImage(size, size, 0)
const scale = 10
/** The canvas containing the 1x image. */
const unscaledCanvas = document.getElementById("unscaledCanvas")
unscaledCanvas.width = size
unscaledCanvas.height = size
unscaledCanvas.style.width = `${size}px`
unscaledCanvas.style.height = `${size}px`
const unscaledContext = unscaledCanvas.getContext("2d")
unscaledContext.putImageData(imageData, 0, 0)

const scaledCanvas = document.getElementById("increasedScale")
scaledCanvas.width = size * scale
scaledCanvas.height = size * scale
scaledCanvas.style.width = `${size * scale}px`
scaledCanvas.style.height = `${size * scale}px`
scaledCanvas.style.imageRendering = "pixelated"
const scaledContext = scaledCanvas.getContext("2d")
scaledContext.imageSmoothingEnabled = false
scaledContext.mozImageSmoothingEnabled = false
scaledContext.webkitImageSmoothingEnabled = false
scaledContext.drawImage(unscaledCanvas, 0, 0, size * scale, size * scale)

const image = document.getElementById("output")
image.width = size * scale
image.height = size * scale
scaledCanvas.toBlob((blob) => {
  const dataURL = URL.createObjectURL(blob)
  image.onload = () => {
    URL.revokeObjectURL(dataURL)
  }
  image.src = dataURL
})
<canvas id="unscaledCanvas"></canvas>
<canvas id="increasedScale"></canvas>
<img id="output" />

javascript canvas
1个回答
0
投票

图像中没有您看到的编码的抗锯齿伪像,您的浏览器在放大图像以匹配 2 倍显示器的像素密度时会创建这些伪像。

我们可以通过在另一个

<canvas>
上再次放大相同的图像来证明这一点:

(async () => {
  // using OP's produced image
  const resp = await fetch("https://i.sstatic.net/7oZk5oeK.png");
  const blob = await resp.blob();
  const bmp = await createImageBitmap(blob);
  const canvas = document.querySelector("canvas");
  // Make the canvas the correct density
  canvas.width *= devicePixelRatio;
  canvas.height *= devicePixelRatio; 
  const ctx = canvas.getContext("2d");
  ctx.scale(devicePixelRatio, devicePixelRatio);
  // no smoothing
  ctx.imageSmoothingEnabled = false;
  // upscale
  ctx.scale(10, 10);
  ctx.drawImage(bmp, -100, -100);
})().catch(console.error);
canvas { width: 300px; height: 150px; }
<canvas></canvas>

请注意,对于此画布,我根据当前 dPR 调整

<canvas>
width
height
的大小,同时通过 CSS 缩小其尺寸,从而避免了调整大小问题。

要在

<img>
中正确渲染图像,您可以准备具有不同密度的多个版本,然后使用 srcset
<img>
 属性:

<img 
  width=160 height=160
  srcset="
    https://i.sstatic.net/7oZk5oeK.png,
    https://i.sstatic.net/OlFcowA1.png 2x,
    https://i.sstatic.net/QsWfkvSn.png 3x,
  ">

或者您可以使用您在

image-rendering
上使用的相同
<canvas>
CSS 规则:

img { image-rendering: pixelated }
<img src="https://i.sstatic.net/7oZk5oeK.png">

© www.soinside.com 2019 - 2024. All rights reserved.