Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime POI sprite colors #776

Merged
merged 6 commits into from
Feb 15, 2023

Conversation

quincylvania
Copy link
Contributor

With #764 merged, we can now set the color of monochrome icons at runtime without a loss in quality. This PR enables such functionality for POI icons. This comes with a number of advantages as we build out the POI layer:

  • Managing colors is much easier when they're defined by variables rather than in dozens of individual SVG files
  • We could use the same icon in different colors without adding more sprites (e.g. RV park vs. RV dealership)
  • We could support multiple color schemes with a single sprite sheet (e.g. dark theme or high contrast)

I was able to generalize some code from the shield-drawing for this. I'm definitely open to suggestions around the implementation, I'm not very picky about what code lives where.

Comment on lines +18 to +19
"poi\nsprite=poi_military_plane\ncolor=" + Color.poi.transport,
"poi\nsprite=poi_plane\ncolor=" + Color.poi.transport,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than the hospital and medical facility icons, I think these icons are now equivalent to SDFs. Instead of sending POI icons through the missing images handler, we should leverage the built-in recoloring properties to achieve the same effect more performantly.

When sprites.js generates the spritesheet JSON file, it should set sdf to true for these POI icons. Then this layer can use the real image names but set the icon-color and icon-halo-color properties.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I experimented with SDFs in the past for icons and I had major problems making them work. I believe they don't anti-alias but I could be wrong on that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends how you generate them. mapbox/mapbox-gl-js#1594 (comment) has some tips on generating a real SDF. A monocolored PNG with alpha transparency is technically not an SDF but it’s close enough to pass for one. You can probably get around the antialiasing issue by upscaling the PNG and using icon-size to bring it back down to a normal size.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm skeptical that the SDF format can support an alpha channel - can you confirm or deny that? The discussion in that mapbox link doesn't sound promising. If it doesn't, we may be better off adding support in maplibre to implement icon-color for re-coloring non-SDF images.

Copy link
Member

@1ec5 1ec5 Feb 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As GL JS understands it, an SDF is a PNG that consists of all black in the RGB channels and a silhouette in the alpha channel – in other words, the inverse of a mask. If by “an alpha channel” you mean the ability to make part of the icon translucent while the rest is transparent or opaque, then no, I don’t think GL JS supports that, but would we need that for POI icons?

The main advantage of using SDFs is that both the icon and its halo can scale without getting pixelated, but you’ll get better results by downscaling than by upscaling. The main limitation of SDFs is that GL JS only supports single-color SDFs, not multicolor SDFs.

A more general-purpose icon-color would be interesting, but I don’t think it should block this PR, nor would I discount SDFs without giving it a try. Another alternative, if this recolored-monocolor aesthetic is desired, would be to use a custom font, but that’s a bit overkill for just a few fonts. (That demo takes a custom font and converts it into SDFs.)

/ref mapbox/mapbox-gl-style-spec#97 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If by “an alpha channel” you mean the ability to make part of the icon translucent while the rest is transparent or opaque, then no, I don’t think GL JS supports that, but would we need that for POI icons?

My thinking, and I could be totally wrong here, is that an icon with diagonal lines will necessarily create pixels that are an alpha blend between the icon and whatever is behind it. And so an SDF which is pixel-on or pixel-off would make the icon look worse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alpha channel can contain pixels that are between 0% and 100%; that much is supported. What probably isn’t supported is to have a whole region of the image be filled with 50% transparency in the alpha channel.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes that sounds workable then. I was concerned about making sure icons could be properly anti-aliased.

So something like this?
https://github.com/dy/svg-path-sdf

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could work, though it’s also possible to do this manually. The SVGs currently in this PR branch would be a fine starting point:

  1. Stick a white background in the SVGs and delete the halos.
  2. In sprites.js, do this to each image’s data buffer before writing it out to a file.
  3. Set icon-halo-* properties on the symbol layer to restore the halos that had been in the SVG.

(Now these halos would be recolorable independently of the rest of the icon, so we could even implement a sometimes-halo, sometimes-knockout like on some text labels.)

# Conflicts:
#	src/js/shield.js
Copy link
Member

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concerns about this event-based approach were poorer performance than a built-in sprite approach, coupled with making the format for sprite names even more complex. However, the implementation in this PR has the advantage of compatibility with all the POI icons, including the medical icons that currently require two colors (or, seen another way, have an abnormally thick halo in the middle).

I still think the SDF approach is worth exploring sooner or later, definitely before we branch out to things like a Dark Mode style or start varying the icon colors by zoom level. But at least the current workflow is familiar to enough of this project’s contributors.

@quincylvania
Copy link
Contributor Author

@1ec5 Thanks for outlining the SDF option, I wasn't aware of that GL JS functionality. I agree that this PR is a bit hacky—although it's consistent with the shield-rendering code—and it'd be great to try implementing a built-in solution at some point. I'm particularly interested in run-time icon halos.

@ZeLonewolf
Copy link
Member

I believe we need an update in the contributor's guide with this change. Currently it instructs POI contributors to color their SVG icons, whereas now they need to make black and white icons but assign them to one of the POI categories.

@quincylvania
Copy link
Contributor Author

I believe we need an update in the contributor's guide with this change.

Done. Let me know if anything needs clarity.

Copy link
Member

@ZeLonewolf ZeLonewolf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@ZeLonewolf ZeLonewolf merged commit f0f63e7 into osm-americana:main Feb 15, 2023
@quincylvania quincylvania deleted the runtime-poi-colors branch February 15, 2023 02:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants