mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.16k stars 2.21k forks source link

Text halos do not overlay other text in the layer #4709

Open jdeboer-geoplan opened 7 years ago

jdeboer-geoplan commented 7 years ago

Hi, I'm not sure this is a bug because I'm probably using mapbox in an unusual way, but I think this would hold good for any overlapping text.

I was using text symbols with a custom font to display color-able map points. This works great but not when there are a lot of points close together. By putting a text-halo on the style I get an outline but from what I understand of the code all the halos are drawn then the text drawn on top. This means they don't overlap and give a distinct outline. Is there a better way of doing this? Would a text-stroke differentiated from text-halo that is drawn at the same time as the text be a good way to go?

image

Amazing work guys.

stevage commented 7 years ago

Just to check I understand what you're saying: the pink teacups are text symbols, their black outlines are text halos, and you would like to see those black outlines separating the teacups when they overlap each other?

jdeboer-geoplan commented 7 years ago

Yup that'd be my ideal. Something like what was accidentally achieved in https://github.com/mapbox/mapbox-gl-js/issues/596#issuecomment-49957625

jfirebaugh commented 7 years ago

Right -- thanks for linking that issue @jdeboer-geoplan. The current behavior was chosen because it's almost always the desired result for text symbols. If we were to implement the alternative behavior, we would need to take care to preserve the current rendering of text. I can think of a few approaches:

jdeboer-geoplan commented 7 years ago

Hi, thanks for taking a look, taking those in turn. 1) icon halo overdrawing should probably happen but this wouldn't fix my particular case. Unless by icon you meant single character text, then that would work but sounds dirty. 2) would probably be simplest to implement but at the expense of having another property on the style that wouldn't make sense to set in the vast majority of cases. 3) sounds the best, it would allow overlapping text to be readable whilst not messing up any individual label. Possibly difficult to make performant?

mb12 commented 7 years ago

@jdeboer-geoplan Why are you not using custom sprite or Map.addImage/Map.removeImage?

jdeboer-geoplan commented 7 years ago

Hi @mb12 possibly because I'm not sure what I'm doing. I need these 'icons' to be changeable, colourable and at different sizes. I'm pretty sure I can't recolor with a custom sprite - don't understand the SDF thing and I need multi color icons (non-recolorable) on the map. I'm not sure about using the Images, I'll look into it. Thanks

kkaefer commented 7 years ago

See https://github.com/mapbox/mapbox-gl-js/pull/4717

lbud commented 7 years ago

I can think of a few approaches:

  • Make halo overdrawing happen for icons, but not for text
  • Make halo overdrawing an opt-into behavior based on a style property
  • Make halo overdrawing happen for separate features, but preserve the current behavior for individual icons/glyphs associated with a single feature

Based on @d1manson's use case in #4717 it is clear that the first option doesn't adequately cover possible desired behaviors. I'd be inclined to go with the second option, although this wouldn't necessarily wholly cover #4717 either (it would require text-letter-spacing to be set widely enough, as noted in that PR). @ChrisLoer @ansis (or anyone else on @mapbox/gl) do you have thoughts on desirability/feasibility of the third?

ChrisLoer commented 7 years ago

@lbud The third behavior sounds desirable to me. Maybe we could change the z-position by feature to allow one to clobber the other without exploding the number of draw calls? Tricky part is whatever z-ordering we did would have to be consistent across tiles.

ansis commented 7 years ago

The third sounds good.

Luckily we just need to:

To draw both the main glyphs and halos as separate quads within one draw call we'd just sneak a single bit into some attribute that distinguishes whether a quad is halo or not. This would be used to select the right colour/width/blur in the shader. We'd pass both versions for every glyph (we already do this in -native). We don't need both the halo and regular colour for a single glyph, but reducing this duplication would be tricky with our data driven styling implementation.

perf:

We should wait for https://github.com/mapbox/mapbox-gl-js/issues/4718 before trying this.


I don't think we can use any depth tricks since text rendering has semi-transparent fragments. Depth can only be used to drop fragment, not re-order them.