pents90 / eburp

The Eight-Bit Universal Role Playing Engine -- the technical masterpiece behind the hit RPG "Gurk III"!
120 stars 25 forks source link

How does the ImageProcessor class render its effects? #3

Closed justindujardin closed 10 years ago

justindujardin commented 10 years ago

The ImageProcessor class is pretty cool, and produces cool effects, but the code is kind of obfuscated with no docs.

I've got a fork that uses scaled sprites with no filtering, to avoid producing spritesheets of each scale for the majority of the game icons. This works great for most things, but whenever an ImageProcessor effect is applied the sprites disappear.

It seems to iterate over the entire screen and do some rendering, but looking at the code I'm not sure why it's working this way. It'd help me out greatly in debugging this issue if there was a conceptual overview of how the ImageProcessor does its rendering. Just a few words in this issue would be sufficient.

Thanks for the cool engine, it's' fun to play with.

pents90 commented 10 years ago

Yeah, it's pretty dense in there. What it is doing is drawing the icon with effects to a separate, hidden canvas. It can draw a "glow" or "halo" around an icon, or it can overlay or "paint" a color on to the icon. That would be fairly simply on its own, but it also allows multiple glows or overlays, mixing them radially. So if, for example, a combatant is paralyzed and poisoned during combat, it will be colored both blue and green.

For the paint algorithm-- it simply applies a basic "dodge" blend (really just adding color channels) to all non-black pixels in the image.

For the glow algorithm-- it computes how far a transparent pixel is from the nearest non-transparent one and colors it based on that distance.

It is a bummer to have sprite sheets bundled for all the different pixel sizes. I had originally hoped I could simply scale the base sprites using a nearest-neighbor algorithm, but unfortunately this wasn't supported on all web platforms. Were you able to get that to work? If so, the ImageProcessor algorithms should all still work, you will just need to set Screen.SCALE to 1.

justindujardin commented 10 years ago

Thanks for the info, I think I can wrangle this now. I wasn't aware it was rendering to another canvas. That's clever.

I'm still looking in to the feasibility of scaling the sprites. Here's what I've found:

Most sprites can be scaled if your canvas supports nearest-neighbor scaling. This includes most game specific sprites (icons0, icons1), but excludes certain images like the screen overlay and buttons.

I've found that there are canvas context properties that can enable nearest-neighbor, and a set of CSS options that attempt to do the same. I've bundled them both together and called it good. I don't know how great the combined platform support is for this configuration, but it works in my testing. For my case this is acceptable, and I will deal with it more thoroughly if I try to ship anything commercially.

context.mozImageSmoothingEnabled = false
context.webkitImageSmoothingEnabled = false

and

canvas {
    image-rendering: optimizeSpeed;
    image-rendering: -moz-crisp-edges;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: -o-crisp-edges;
    image-rendering: optimize-contrast;
    -ms-interpolation-mode: nearest-neighbor;
}
pents90 commented 10 years ago

Yeah, that sounds like the right approach. I had hit the wall because I was trying to support some older Android devices. But it's definitely doable on web, and it may be fine now on modern Android/iOS devices too.