Reputation:
When drawing an image with rotation on a JS canvas the image is blurring. See attached images.
I have set imageSmoothingEnabled
to false
and image-rendering
to pixelated
.
Render code:
fg.save();
fg.translate(gun.x - gun.frame.w / 2, gun.y - gun.frame.h / 2);
fg.rotate(gun.a);
if(mouse.x < (player.x + player.width / 2)) {
fg.scale(1, -1);
}
fg.drawImage(spriteSheet, gun.frame.x, gun.frame.y, gun.frame.w, gun.frame.h, 0, 0, gun.frame.w, gun.frame.h);
fg.restore();
Please no answers saying it isn't possible. It would be much more helpful having an alternative rendering method suggested.
Upvotes: 1
Views: 52
Reputation: 54099
Even when smoothing is off and canvas image is pixelated, the canvas 2D renderer will still anti-alias the edge of an image rendered using ctx.drawImage
. This is true even if the image is part of a sprite sheet and you use the overload drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
Any visible pixels that touch the edge will be aliased. This cannot be disabled.
The solution is to add a transparent boundary around each sprite and draw them including the transparent boundary.
This will ensure no visible pixels are touching the edge of the rendered image.
Image shows sprites as defined in the example snippet below.
Red rectangles no transparent padding and green rectangles padded with a one-pixel transparent boundary.
The code below renders the gun sprite onto a zoomed in canvas (8 times)
On the...
const ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
const sprites = { // ax, ay are anchors as fraction of sprite size
// Without transparent padding
gun: {x: 19, y: 1, w: 16, h: 8, ax: 0.375, ay: 0.5},
cowboy: {x: 1, y: 1, w: 16, h: 16, ax: 0.5, ay: 1},
// With transparent padding
gunPad: {x: 18, y: 0, w: 18, h: 10, ax: 0.375, ay: 0.5},
cowboyPad: {x: 0, y: 0, w: 18, h: 18, ax: 0.5, ay: 1},
};
// draws sprite with anchor at x, y and rotated around anchor
function drawSprite(sprSheet, spr, x, y, rotate) {
const ax = Math.cos(rotate);
const ay = Math.sin(rotate);
ctx.setTransform(ax, ay, -ay, ax, x, y);
const cx = spr.ax * spr.w;
const cy = spr.ay * spr.h;
ctx.drawImage(sprSheet, spr.x, spr.y, spr.w, spr.h, -cx, -cy, spr.w, spr.h);
}
function drawGuns() {
const ang = Math.atan2(-1, 2);
drawSprite(spriteSheet, sprites.gun, 8, 8, ang);
drawSprite(spriteSheet, sprites.gunPad, 28, 8, ang);
}
// Image source https://stackoverflow.com/questions/79426201/pixel-image-blurs-on-rotation-js-canvas#
// Copy of your cowboy and gun sprite with padding
const spriteSheet = new Image();
spriteSheet.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAASCAYAAAAzI3woAAAAAXNSR0IArs4c6QAAAXlJREFUSEvNVrFKBEEMzZw/YGmnYqNcq+BniBbqh1jJISJX+SeC4p9YHdprYWkh2Agrb+Et2VxmJnNc4TZ3OzN5eclLMpvknz3J43N1cNbp9fvXx6TX8B6Nw2KV7IC7BAyA6fSztzs9+ZKn501ZLLYEa3jHczfflxIpYDAIbVciQ8yBkAWhMUloMBrTRu8xIB0Egio98DEipEFgyAzlQOCM5/DfZot4dBSVd5BMA1CiEghl4FlNyCND+SK1N5IsqjfIemRmh+fd3s7HUH8laXMBj4q6pSMAaCO+Obrodrffe1+elBHpXELIFItSg3CNNWQJ3R5fdj/fv5ImIpONJPOXh/B4oB+X0Oz6ra96/Nru4p6XoUgGameyc8hr91zt1Jy07C9liHJ57V9r9xbH1aLWExqHvRliu7A2sVch2GcIZFgvdqp60uFM9BppJTUQYvSIOjepvbtt3VkaDUZ2Tm0e6ds/Mn1bshT6/LCA6yah8ZsHV0u0q5z9A9xzCSK+FV/aAAAAAElFTkSuQmCC";
spriteSheet.addEventListener("load", drawGuns);
canvas {
width: 384px;
height: 128px;
image-rendering: pixelated;
}
<canvas id="canvas" width="48" height="16"></canvas>
If the rotated image is still too low quality, you should consider hand drawing the rotated image. Personally, I find hand drawn rotation more esthetically pleasing than machine rotated.
Upvotes: 2