Repository: microsoft/terminal
File: src/cascadia/TerminalControl/TermControl.cpp (C++)

Code before:
        {
            solidColor.Color(bg);
        }

        BackgroundBrush(RootGrid().Background());

        // Don't use the normal BackgroundBrush() Observable Property setter
        // here. (e.g. `BackgroundBrush()`). The one from the macro will
        // automatically ignore changes where the value doesn't _actually_
        // change. In our case, most of the time when changing the colors of the
        // background, the _Brush_ itself doesn't change, we simply change the
        // Color() of the brush. This results in the event not getting bubbled
        // up.
        //
        // Firing it manually makes sure it does.
        _BackgroundBrush = RootGrid().Background();
        _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"BackgroundBrush" });

        _isBackgroundLight = _isColorLight(bg);
    }

    bool TermControl::_isColorLight(til::color bg)
    {
        // GH#13450 function to check if the current background color is light
        // enough to need a dark version of the visual bell indicator
        return 127 < (bg.r + bg.g + bg.b) / 3;
    }

    // Method Description:
    // - Update the opacity of the background brush we're using. This does _not_
    //   update the color, or what type of brush it is.
    // - INVARIANT: This needs to be called on the UI thread.
    void TermControl::_changeBackgroundOpacity()
    {
        const auto opacity{ _core.Opacity() };
        const auto useAcrylic{ _core.UseAcrylic() };

        // GH#11743, #11619: If we're changing whether or not acrylic is used,
        // then just entirely reinitialize the brush. The primary way that this
        // happens is on Windows 10, where we need to enable acrylic when the
        // user asks for <100% opacity. Even when we remove this Windows 10
        // fallback, we may still need this for something like changing if
        // acrylic is enabled at runtime (GH#2531)
        if (auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>())
        {
            if (!useAcrylic)
            {
                _InitializeBackgroundBrush();
                return;
            }
            acrylic.TintOpacity(opacity);

Review comment (performance):
A better, but still cheap (to code) approach for calculating the lightness/luma of `bg` would be to use the Rec. 601 coefficients directly without converting to linear RGB first. This:
```cpp
bool TermControl::_isColorLight(til::color bg) noexcept
{
    // GH#13450 function to check if the current background color is light
    // enough to need a dark version of the visual bell indicator
    // This is a poor man's Rec. 601 luma.
    const auto l = 30 * bg.r + 59 * bg.g + 11 * bg.b;
    return l > 12750;
}
```

Code after:
        {
            solidColor.Color(bg);
        }

        BackgroundBrush(RootGrid().Background());

        // Don't use the normal BackgroundBrush() Observable Property setter
        // here. (e.g. `BackgroundBrush()`). The one from the macro will
        // automatically ignore changes where the value doesn't _actually_
        // change. In our case, most of the time when changing the colors of the
        // background, the _Brush_ itself doesn't change, we simply change the
        // Color() of the brush. This results in the event not getting bubbled
        // up.
        //
        // Firing it manually makes sure it does.
        _BackgroundBrush = RootGrid().Background();
        _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"BackgroundBrush" });

        _isBackgroundLight = _isColorLight(bg);
    }

    bool TermControl::_isColorLight(til::color bg) noexcept
    {
        // Checks if the current background color is light enough
        // to need a dark version of the visual bell indicator
        // This is a poor man's Rec. 601 luma.
        const auto l = 30 * bg.r + 59 * bg.g + 11 * bg.b;
        return l > 12750;
    }

    // Method Description:
    // - Update the opacity of the background brush we're using. This does _not_
    //   update the color, or what type of brush it is.
    // - INVARIANT: This needs to be called on the UI thread.
    void TermControl::_changeBackgroundOpacity()
    {
        const auto opacity{ _core.Opacity() };
        const auto useAcrylic{ _core.UseAcrylic() };

        // GH#11743, #11619: If we're changing whether or not acrylic is used,
        // then just entirely reinitialize the brush. The primary way that this
        // happens is on Windows 10, where we need to enable acrylic when the
        // user asks for <100% opacity. Even when we remove this Windows 10
        // fallback, we may still need this for something like changing if
        // acrylic is enabled at runtime (GH#2531)
        if (auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>())
        {
            if (!useAcrylic)
            {
                _InitializeBackgroundBrush();
                return;