Menardi / whosthatpokemon

A simple HTML5 game that generates silhouettes from images and gets the user to guess the name of the Pokémon shown.
https://gearoid.me/pokemon
86 stars 26 forks source link

Don't use gross upscaling on pixel sprites #49

Closed pressRtowin closed 5 months ago

pressRtowin commented 8 months ago

What the title says. Pixel art should be scaled with nearest neighbor.

pressRtowin commented 5 months ago

@Menardi Most sprites look better now, but there's a fuzziness to them when looking closer. Also some sprites are shown disproportionately smaller than others.

Also, I tried saving a few of them to have a closer look at them, and it looks like some sprites are pre-scaled (some were scaled 16x (or is it 4x? Every 1x1 pixel was represented by 4x4 pixels 😅). Perhaps there are some other scalings mixed in too?) while others were not (1x) (for example, slakoth was 16x, while solgaleo was 1x).

image

image

I feel like ideally, all sprites should just be stored as 1x and scaled up with nearest neighbor to fit within a boundary, so there aren't huge discrepancies in the size? And some way to remove that fuzziness would be nice. Here's an example of that (took a screenshot and zoomed into it):

image

image

I use stylebot on my browser to force all scaling to nearest neighbor when zooming in, so it's not something with my browser that I can change at this point. But since when I save the actual files and look at them, they're all pixel-perfect (besides the nearest neighbor upscaling baked into some sprites), it must be something with how the page is loading the images.

Menardi commented 5 months ago

The scaling of the images is handled by the browser and I don't have a huge amount of control over it. The images are drawn to a canvas element at exactly four times their file resolution, to ensure we're not introducing any subpixel rendering. It's up to the browser to figure out how to scale it from there.

In the most recent update, I did change some settings to suggest to the browser that these sprites shouldn't get image smoothing, and I think it does look a lot better. From here, it could be down to the browser's specific implementation or even how the OS renders to screen.

If you have any specific suggestions of improvements that could be made in the code, I'm happy to take a look at any pull requests that would improve this.

pressRtowin commented 5 months ago

at exactly four times their file resolution

due to the differences in the baked-in scaling (such as between slaktoh and solgaleo), this unfortunately results in a giant slakoth and tiny solgaleo (among others) for me.

I'm not sure what it could be in my browser that would cause this. Every site that I've tested so far respects my stylebot settings:

img {
  -ms-interpolation-mode: nearest-neighbor;
  image-rendering: pixelated;
}

You can see that when I zoom into Bulbasaur's 1st gen sprite on Bulbapedia:

image

no fuzziness is introduced:

image

Furthermore, I'm seeing this fuzziness here without any amount of additional zoom. Zooming isn't introducing any additional fuzziness. Due to my stylebot setting, my browser is only scaling up the existing fuzziness in a pixel-perfect way (whereas browser-introduced fuzziness usually scales with additional zoom).

Are you sure the method you're using to draw the element at 4x isn't introducing some kind of filtering?

pressRtowin commented 5 months ago

I found a fix!

When inspecting the canvas element, I found this:

image

Here's how the sprite looks:

image

I simply changed that to this:

image

and now the sprite looks like this:

image

I'm not sure why stylebot wasn't overriding that specific bit of css, but that was the culprit.

Edit: Oh, because it's a canvas element not img. lol

Fixed in stylebot:

image

pressRtowin commented 5 months ago

I submitted a PR for this as well. I'm not actually sure if the -ms-interpolation-mode part is needed. I know the reason it was included in that stylebot code is due to different browsers looking for different parameters when deciding how to scale images, and it was included to cover all bases, so maybe it should be kept in the canvas element too for good measure?

Menardi commented 5 months ago

Thanks for the PR! I think I originally tested image-rendering: crisp-edges, but it turns out it has poor support. image-rendering: pixelated works much better, thanks for the suggestion. I'm deploying a fix now.

pressRtowin commented 5 months ago

Just did some research and looks like -ms-interpolation-mode was depreciated in IE9 so chances are, that won't affect anyone 🤣

pressRtowin commented 5 months ago

So actually paying attention, I noticed that it seems to be only the gen 6/7 sprites that aren't getting scaled up?

But Solgaleo's sprite scaled up 400% fits in almost the same space as Wailord's sprite:

image

so is there a particular reason for this?

Menardi commented 5 months ago

Good spot! It seems the Gen 6 and 7 sprite images were all 256x256, with a lot of transparent padding around them. I've cropped them with ImageMagick, making the smaller sprites 96x96 to match the other generations. Some were too big to fit in that space, so I've made them the minimum square size possible without rescaling them.

I've also updated the canvas scaling code to use a dynamic multiplier based on the sprite size, rather than always multiplying by 4 (in b8ae040). This should fix the issue.

pressRtowin commented 5 months ago

I've also updated the canvas scaling code to use a dynamic multiplier based on the sprite size, rather than always multiplying by 4 (in https://github.com/Menardi/whosthatpokemon/commit/b8ae040d37bf547bdc71df6ea663c9ae3f7c59b4). This should fix the issue.

@Menardi I think there may be a bug here? So I was playing through just gen 1 to test things out, and suddenly Kakuna loaded and this happened:

image

Literally jump scared by Kakuna 😅

Menardi commented 5 months ago

Whoops, sorry for the scare! :joy: The code was only checking if the image's width was less than 200px to determine if it was a sprite, and Kakuna happens to be a particularly narrow Pokémon at 189px, so the game treated it as a sprite.

I've fixed this in ee0c235 by now checking for the larger of width and height when doing the comparison. Thanks for the bug report!