I am trying to scale a 1x image up to an arbitrary size using a canvas. However, the image always has some interpolation at the edges of each pixel. I expect the image provided to match how the look canvas exactly. No combination of different scales etc. has worked for me. Even right click -> save image on the canvas produces the same image.
I have also tried using toDataURL
on the canvas but I get the same result.
// 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")
// Allow a pixel multiplier > 1 to not be blurry.
scaledCanvas.imageSmoothingEnabled = false
scaledCanvas.mozImageSmoothingEnabled = false
scaledCanvas.webkitImageSmoothingEnabled = false
scaledCanvas.style.imageRendering = "pixelated"
scaledCanvas.width = size * scale
scaledCanvas.height = size * scale
scaledCanvas.style.width = `${size * scale}px`
scaledCanvas.style.height = `${size * scale}px`
const scaledContext = scaledCanvas.getContext("2d")
scaledContext.imageSmoothingEnabled = 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" />