Search code examples
spritescalep5.js

Blurry sprites when scaled up in p5.js


I'm trying to scale the size of a sprite in p5.js up a bit (factor of 2) and they look blurry when rendered. Obviously scaling up is usually not a great idea, however I've had success in making a pixelated sprite look crisp in raw JavaScript (based on this article: https://nluqo.github.io/broughlike-tutorial/stage1.html).

In JS I'd do something like this:

let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;

and drawing like this:

let tileSize = 32;
ctx.drawImage(
  spritesheet,
  sprite*16,
  0,
  16,
  16,
  x*tileSize,
  y*tileSize,
  tileSize,
  tileSize
);

and when scaling sprites upwards, I get a nice clean effect (similar to how it is shown in the article I linked above).

Now I'm trying to port some code over to P5.js for demonstration purposes and have been trying to replicate the above upscaling (spritesheet is 16x16, would like to render as 32x32) and have been trying to replicate the typical ctx calls, however they don't seem to work the same. Also of note is that I have tried to grab the canvas element from createCanvas and get a 2D context, but the method doesn't exist.

Here is what I've tried so far:

createCanvas(1024, 800);
noSmooth();
...
const spriteSize = 16;
const spriteScaled = 32;

let _c = 0; // col lookup into spritesheet
let _r = 0; // row lookup into spritesheet
test_image = createImage(spriteScaled, spriteScaled);
test_image.copy(
  tileMap,
  _c * spriteSize,
  _r * spriteSize,
  spriteSize,
  spriteSize,
  0,
  0,
  spriteScaled,
  spriteScaled
);
// also tried this:
// test_image.resize(spriteScaled, 0);

Additionally, I added this in my style.css to no effect:

canvas {
  display: block;
  image-rendering: -moz-crisp-edges;
  image-rendering: -webkit-crisp-edges;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}

Solution

  • While the noSmooth() function does affect images drawn with the image() function, it does not affect the behavior of the copy() function.

    let imgUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAAh0lEQVQ4y92TMQ6AIAxFX40bB3HyIE4ex00jm+dj8iDOdRGDWhMim39seeWT8gVbatSEjKIC6DY+DzpvMnUKR3AN/mnp6Inzmg6RFLbAu5p2jG4EoIqNHNhSBegy5QNr8PE5enHwVT8YcK5xmaDv+L7GUgek63xzcr/9NQvWvxhmOz9SmsZi7UmSK41htVcqAAAAAElFTkSuQmCC";
    
    const spriteSize = 16;
    const spriteScaled = 32;
    
    let tileMap;
    let test_image;
    
    function preload() {
      tileMap = loadImage(imgUrl);
    }
    
    function setup() {
      createCanvas(1024, 800);
      noSmooth();
    
      let _c = 0; // col lookup into spritesheet
      let _r = 0; // row lookup into spritesheet
      test_image = createImage(spriteSize, spriteSize);
      // Don't resize when copying. This will cause interpolation to happen.
      test_image.copy(
        tileMap,
        _c * spriteSize,
        _r * spriteSize,
        spriteSize,
        spriteSize,
        0,
        0,
        spriteSize,
        spriteSize
      );
    }
    
    function draw() {
      // Only resize when you actually draw the sprite
      image(test_image, 0, 0, spriteScaled, spriteScaled);
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>