Page MenuHome

Automatic 3DView Overlay Colors

Authored by Harley Acheson (harley) on Feb 2 2019, 10:00 PM.
"Like" token, awarded by monio."Like" token, awarded by billreynish."Like" token, awarded by Tetone."Like" token, awarded by amonpaike."Like" token, awarded by duarteframos.



Something to play with, consider, or talk about...

The central premise here is that the colors of the overlays on the 3DView window can no longer be set by the theme. This is because the background of the 3DView can be specified by the world color, the viewport color, or the theme color. Therefore you can pick any theme you wish with a background color that you like, but that color doesn't stay as you can select a different color without changing theme. For example changing it with Properties / World / Viewport color. Or Shading / Background / Viewport color.

This issue is noticed the most by simply changing from the Default workspace to "2D Animation" workspace, since doing so changes the background color via the viewport color.

The following image is a capture of how it is now with default theme before and after changing to the 2D workspace. You'll notice that the overlay text is always white. There is an assumption in the code that TH_TEXT and TH_TEXT_HI are always white in all themes. And therefore the shadow is hard-coded to black. So the only reason it is always visible is because the shadow is so large. The icons of the gizmos are always white because icons always use TH_TEXT for color and so can't change as the viewport color changes. And the color of the highlight circle behind the icons doesn't change, which makes it hard to see with some color combinations.

This patch selects text colors, shadows, and scrim color automatically based on the color of the background, regardless if set in work, viewport, or theme. So the are always nicely visible regardless of what is chosen. And it tries to harmonize the colors between overlays and gizmos.

The following captures what it looks like after the patch is applied and changing from default them to the 2D workspace:

You'll notice that text is a light color on the dark background, but dark on the light background. The icons also change color, and the overall look of gizmos when selected or not seems consistent.

I've tested it will all current themes and with any colors I could think of and it always remains nicely readable.

Patch details:

view3d_utils.c. Enhanced ED_view3d_background_color_get() so that it can work earlier in the space-creation process, and will also take into account gradient backgrounds. Added new helper functions to create contrasting colors from background color

space_view3d.c. New procedure that gets a nice contrasting text color from background color and overwrites the 3dview main area's TH_TEXT and TH_TEXT_HI. This is called minimally by responding to NC_WINDOW (them color changes), ND_SPACE_VIEW3D (Viewport color changes), and ND_WORLD (world color changes).

view3d_draw.c & view_draw_legacy.c. Drawing the view name and other overlays using a text shadow that contrasts with text color, not just hardcoded to black.

view3d_gizmo.navigate.c. Moves color-setting code from WIDGETGROUP_navigate_setup and into a new WIDGETGROUP_navigate_refresh so it can update the widgets if the background changes

Diff Detail

rB Blender

Event Timeline

You've done it again @Harley Acheson (harley). Great little readability improvement. Tested here - it seems to work well.

This revision is now accepted and ready to land.Feb 2 2019, 10:04 PM

Thanks @William Reynish (billreynish)!

About the only thing "controversial" in here is the idea of overwriting the area's theme colors TH_TEXT and TH_TEXT_HI and making their values dynamic instead. But I like that idea the most as it means that anything trying to write to the 3D viewport, even with python, will get the benefit if they use those colors.

But it does mean that when you go to the theme settings for those two colors (Themes / 3D View / Theme Space) it will ignore you as you try to change them. I'm not sure if there is any easy way to remove those items from theme settings or if that is important at all. I guess we'll see what Brecht thinks.

I think that's perfectly fine. IMO. as you say, the background color cannot be relied upon to be anything specific here.

I did notice a major flaw though: If you switch to LookDev or Rendered view, it still looks for the background color from the Workbench. This is obviously wrong. I think that, in the case of LookDev or Rendered view, where the background may be an HDRI and this impossible to contrast against, we should just use the theme color as we do today.

William Reynish (billreynish) requested changes to this revision.Feb 2 2019, 10:45 PM

LookDev and Rendered view don't work well with this. In these cases just use theme color.

This revision now requires changes to proceed.Feb 2 2019, 10:45 PM
Brecht Van Lommel (brecht) requested changes to this revision.Feb 2 2019, 10:53 PM

In more complex scenes the background color is not so relevant as most of the 3D viewport is covered by objects. In lookdev and rendered mode there is no single fixed color, but a background image texture.

I suggest to try this on some test files:

There always needs to be some shadow or a shape behind the text to keep it readable.


This does not work when there are multiple 3D viewports with different background colors.


No need to use approximations or BT.601 here. On modern CPUs this code might actually be slower.

In more complex scenes the background color is not so relevant as most of the 3D viewport is covered by objects. In lookdev and rendered mode there is no single fixed color, but a background image texture.

I suggest to try this on some test files:

I will give them a try. Thanks

There always needs to be some shadow or a shape behind the text to keep it readable.

I did leave the shadow there, but I reduced it from "5" to "3" and centered it. It is only less noticeable because the text color and shadow colors are picked better. For example we really notice the text shadow now with the white text when it is on a lighter background, but it is also direly needed since it is white text on a lighter background. But when text color contrasts more it is more likely that text shadow is not needed because it is closer to background shadow. So if it is working well we shouldn't notice it. But I did leave it in just in case for any corner cases.

This does not work when there are multiple 3D viewports with different background colors.

I did not test with multiple viewports with different backgrounds. Thank you for noticing. Will look into it.

No need to use approximations or BT.601 here. On modern CPUs this code might actually be slower.

I'm going to assume that is a typo and that that you meant "approximations OF BT.601" and that I should just multiply it out properly. Will do.


  • Replaced gray approximation with more accurate multiplied version
  • Overlay text shadow size increased back to 5 (original value)
  • Scrim behind navigate widget gets lower opacity
  • Scrim color (circle behind widget) always increases contrast

Not yet addressed:

  • Multiple 3D viewports with different background colors
  • Testing on backgrounds that use images instead
Harley Acheson (harley) marked an inline comment as done.


  • Saving Text color, shadow, scrim color in View3D's overlay struct
  • Draw viewport name overlay with View3D text color
  • Draw selected name overlay with View3D text color
  • Clamp text color and shadow colors to (0-1)
  • Gizmo uses View3D's scrim color


Different 3D views can have different viewport colors and overlay text, gizmo scrim have new better behavior

Not working (yet):

If there are multiple 3D views, (just) the icons in the navigation gizmo use last-set color. These always just draw their icon with TH_TEXT so can't easily override. Will look at this more tomorrow.

if Shading Type is Rendered, then ED_view3d_background_color_get() will return an arbitrary dark-ish value that results in strong white text, dark shadows, etc


I added an "icon_color" member to wmGizmo (to go along with "color" and "color_hi"). That way when it is about to draw the gizmo icons it can get the colors from there instead of just using TH_TEXT. Since that is populated from the View3D values this means that the icons can be different if you have multiple 3D viewports open with different backgrounds.

I also changed the calculation of the scrim colors slightly so there is still some contrast at the very extreme of background color. Basically it tries to increase contrast but when it runs out at the end it will reverse a bit.

It seems to be functionally complete.

Functionality wise I think this is working well now. With multiple 3D Views, and the different shading modes, it does the right thing.

Thanks @William Reynish (billreynish)!

Yes, I can't anything that breaks it.

Here it is with one blender window with four 3D viewports. One using theme settings, one with different viewport color, one in Rendered shading mode, and another with a different World display color.

And this is how it looks with each of the samples that Brecht pointed to:

Nice! It's definitely an improvement.

It's pretty hard to get it right in all situations but I guess the only way to solve this is with a background/outline color or automatically inverting them based on what's in the background.

Tried a random file which happened to have a gray background and a keyframe, so it was hard to see:

Maybe we can think of a way to solve this once and for all.

Thanks @Pablo Vazquez (pablovazquez)!

Yes, when that second line, draw_selected_name(), is shown there are some instances where the text is not intended to be white. I did nothing with those because I didn't quite understand their usages. The only thing changed it is that they would use the same shadow as the other overlay text.

It seem that the different colors are when there are keyframes, and it will be either the color TH_TIME_KEYFRAME or TH_TIME_GP_KEYFRAME. The logic there doesn't really make sense to me in view3d_draw.c lines 1208-1224.

But if those colors are important, and specific colors are expected, then I *could* make them dynamically too. So if you said that TH_TIME_GP_KEYFRAME should be green then I could make a dark green or light green depending on the background color and use that instead of the theme color.

Or I could take the specified theme color and try to make it darker or lighter. However that has some pitfalls. It would work correctly if the theme designer used a color with some latitude, like a mid-green so I could shift it into a light or dark green. But if they selected something too dark or light it not work well. Like yellow turns to brown when dark.

So ideally we'd want to *pick* colors for these and run with them. But I think someone would at least have to say "Yes, TH_TIME_KEYFRAME is meant to be green and TH_TIME_GP_KEYFRAME is supposed to be blue" or whatever.

But if those colors are important, and specific colors are expected, then I *could* make them dynamically too.
Or I could take the specified theme color and try to make it darker or lighter. However that has some pitfalls. It would work correctly if the theme designer used a color with some latitude, like a mid-green so I could shift it into a light or dark green.

This is probably opening a can of worms and all kinds of unexpected results and unforeseen situation, not to mention a lot of work IMHO.

It would probably be much simpler and more predictable to just get a good dropshadow/backdrop system working well that would generate some contrasting background or outline color instead of trying to be too clever.

There would probably always be some uncovered obscure corner cases, or situations where important colors would be altered beyond recognition.

Actually there might not be too many worms in the can...

Although I can't actually find the theme settings, it looks like TH_TIME_KEYFRAME is supposed to be a fairly bright yellow, while TH_TIME_GP_KEYFRAME is an ugly bright green.

If it is just these two colors I can manufacture a contrasting color. The yellow will get a bit ugly as it darkens, but as long as you can tell the difference we might be okay and still better off than we are now:

Okay, my previous idea about the keyframe colors were dumb. I cannot manufacture a kind of "yellow" that is recognizable as yellow when shown against a white background regardless of what shadows are used.

But... I did find that I was I was doing with text shadows there was incorrect in that section. For regular text there I had a good text color and matching shadow color. But I used that shadow even if the text were, in those circumstances, bright yellow or green. I now make a contrasting shadow for the actual selected color. That way bright yellow text can still have a dark shadow when shown on a white background. But in testing this I did find that this extreme, and other situations, were easier to read with the text shadow reduced from "5" to "3".

Changes in this diff:

  • Added more/better comments to my color vars in the View3D struct.
  • Also saving background color into View3D - comes in handy later
  • text shadows reduced to 3 from 5. Works much better when the overlay has to be light on light (like yellow on white)
  • draw_selected_name() now creating shadow from the current text color (if even if yellow or green) - works much better

IMO there's probably a limit to how intelligent this feature can reasonably be. Ideally it would check for the average color beneath each button or behind each text string and find the best contrasting color based off of that - and so it would work even with background images or if they are on top of bright or dark objects.

However, it seems like it can be quite a deep rabbit hole. What you have done already seems like a reasonable heuristic to make things more readable in common cases, and I think that's ok.

Yes @William Reynish (billreynish), this method is definitely an improvement in the vast majority of cases. And is now no worse, and probably a bit better, at the extremes.

If we do get this through I can see that I might want to make some small visual (not functional) changes to the navigation rotate widget as gets a bit odd when used on a light background. But easier to tackle that once this is in.

Just updating the patch to compile properly with current version of trunk.

Members of btheme had been renamed so had to change some "btheme->tv3d" to "btheme->space_view3d"

Otherwise works as before

As has been mentioned we can't rely on the background color because the scene content can be any color.

Think we'd be better off to make these contrast more so the content behind them isn't impacting their visibility so much, so we don't have to try make clever guesses that can backfire under certain conditions.


No need to convert all channels.


looks like mistake (using only red), if this is intended, calculate a single value and assign.


use rgb_to_grayscale


use rgb_to_grayscale


Don't think we need this. If a gizmo is an icon - the color can be used.

@Campbell Barton (campbellbarton) Thanks for the review!

Fixes a "copy and paste" mistake where I was using only red channel when calculating an average of two colors. Had intended on using all channels so function will return an average of the top and bottom if a gradient is set for the background. It worked while testing with grayscales obviously but I also want to handle more complex (ugly) combinations. LOL

Now uses supplied "rgb_to_grayscale" inline function rather than rolling my own. Hadn't notice that existed.

Gizmo does need the added icon_color. The gizmos use color and color_hi for the background circles (color for regular, color_hi for hover). The icon color was just using TH_TEXT so here I am assigning generated values for all three.

Campbell Barton (campbellbarton) requested changes to this revision.Mar 2 2019, 5:06 PM

In general not keen on this approach.

We should first:

  • Make existing colors contrast more, increase alpha.
  • Check they work with different themes.
  • Check if draw style needs changing to overlay better on detailed backgrounds.

My impression is the current design is too subtle which looks pleasing on flat backgrounds but needs tweaking to account for unknown content under them.

This revision now requires changes to proceed.Mar 2 2019, 5:06 PM
Harley Acheson (harley) marked 5 inline comments as done.Mar 2 2019, 5:36 PM

I have a feeling you are missing the forest for the trees here.

Check they work with different themes.

The problem we have now is that the background used and seen is now independent of theme. Themes can't help and have no bearing since you can select any theme at all while also having a completely random background. Every theme we can possibly have cannot optimize for unknown and unexpected background colors.

Make existing colors contrast more, increase alpha.

Contrast between two things can only be optimal if we know the colors of those things. Optimal contrast over a dark background is bright white text with a black outline, while black text with white shadow is best on a light background.

This patch just picks the most optimal using the best information available. In the absolute very worst case imaginable it will still do a better job than we currently have when changing to Grease Pencil - white text on white background. In all cases that I tested the contrast was increased, and the text clearly visible, as seen earlier in this thread where I tested with all the sample files.

Oh well, I tried. LOL

Committed some basic changes to increase contrast rBbd1ba2f7a9100a237d0e55dc7ceab218f131a602

The background alpha was too low (20%), and there was no checks of the icon color so it could show black icon on black backdrop.

Tested w/ light & dark theme [default color / white / black] backdrops.

I think this resolves the worst of the issues mentioned here.

Note, there is a missing update where the theme doesn't apply to the gizmo, until it's refreshed. be aware of this when switching themes.

I think this resolves the worst of the issues mentioned here.

LOL, no, it really does not. Not even close.

This patch was not at all about the contrast for the navigation gizmo icons. It was about addressing a shortcoming in a way that worked well across all overlays versus trying to hack one small area "good enough" With mine you would have ended up with both parts of the navigation widget (the buttons and the rotation part) looking like a unified thing along with other overlay pieces. With yours you've only managed to make the buttons look less unified with the rotation gizmo while also making them uglier and more obtrusive.

Oh well, I tried.

It resolved icons not being visible for black/white backgrounds - depending on the theme.

As you point out a full white background wasn't working so well for axis colors, committed changes to address this:
rB301494f94a7fba5100cdfa5d48491c1df82e235a, rB2d468fc072fc0c70f3eaf0fd8d6788700b567734.


I had such beautiful plans for the rotate widget as well. When I was done, everything in the 3D Editor would have been only as obtrusive as necessary when not in use, but gain the same prominence when used, and all would have looked and worked in a unified way. So everything looking like they belonged in the same program, as if they were designed that way from the start rather than an assortment assembled at random.

Or just add layers of lipstick. LOL.

As of your last changes my patch no longer applies so I'm done hitting my head against a wall over it. Closing