dotnet / dotnet/winforms

Correct System.Windows.Forms.ControlPaint.HSLColor.Darker function

Open
#12,586 1 comment 0 reactions 1 assignee Claimed by @Tanya-Solyanik View on GitHub
api-suggestion
Dominant language
C#
Stars
4.9k
Forks
1.1k
Avg merge
20h 23m
Merged PRs (30d)
103

Description

### Background and motivation

At least since .NET Framework 4, the `System.WIndows.Forms.ControlPaint.HSLColor.Darker` function (which is called by `System.Windows.Forms.ControlPaint.Dark()`) has been wrong for non-system colors.

The `Lighter` function logically interpolates, based on the passed percentage value, from the current luminosity towards a luminosity value 50% higher. The `Darker` function illogically interpolates, based on the passed percentage value, from a luminosity value 33% lower than the current luminosity towards zero.

A call to `Lighter(0)` reasonably returns the same color. A call to `Darker(0)` unreasonably returns a color 33% darker. `Lighter(0.5)` returns a color 25% lighter; `Darker(0.5)` returns a color 67% darker. `Lighter(1)` returns a color 50% lighter; `Darker(1)` returns black.

### API Proposal

```csharp
namespace System.Windows.Forms;

public static partial class ControlPaint
{
private readonly struct HLSColor : IEquatable
{
public Color Darker(float percDarker)
{
if (!_isSystemColors_Control)
{
// *** this block is the only changed code ***
// match the Lighter function and use current luminosity as a baseline instead of zero
int zeroLum = luminosity;
int oneLum = NewLuma(ShadowAdjustment, true);
return ColorFromHLS(_hue, zeroLum + (int)((oneLum - zeroLum) * percDarker), _saturation);
}
else
{
// With the usual color scheme, ControlDark/DarkDark is not exactly
// what we would otherwise calculate
if (percDarker == 0.0f)
{
return SystemColors.ControlDark;
}
else if (percDarker == 1.0f)
{
return SystemColors.ControlDarkDark;
}
else
{
ARGB dark = SystemColors.ControlDark;
ARGB darkDark = SystemColors.ControlDarkDark;

return Color.FromArgb(
(byte)(dark.R - (byte)((dark.R - darkDark.R) * percDarker)),
(byte)(dark.G - (byte)((dark.G - darkDark.G) * percDarker)),
(byte)(dark.B - (byte)((dark.B - darkDark.B) * percDarker)));
}
}
}
}
}
```

### API Usage

```csharp
using System.Drawing.Color;

Color c = Color.FromArgb(128, 128, 128);
Color sameColor = System.Windows.Forms.ControlPaint.Dark(c, 0.0);
Color darkColor = System.Windows.Forms.ControlPaint.Dark(c, 0.5);
Color darkerColor = System.Windows.Forms.ControlPaint.Dark(c, 1.0);

Color approxSameColor = System.Windows.Forms.ControlPaint.Light(System.Windows.Forms.ControlPaint.Dark(c));
Color approxSameColor2 = System.Windows.Forms.ControlPaint.Dark(System.Windows.Forms.ControlPaint.Light(c));
```

### Alternative Designs

_No response_

### Risks

_No response_

### Will this feature affect UI controls?

Yes.
- no
- none
- no

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.