6

In earlier versions of Windows, it was easy for users to set the exact colors of interface elements. For example, in Windows 7, you could go to [Control Panel -> Personalization -> Window Color], which would bring up a dialog box letting you select specific interface elements and set the color for each of them. In Windows 10, much of that functionality was removed; you can go to [Settings -> Personalization -> Colors], but it will only let you choose an accent color as an overall theme, and the exact colors of interface elements are then all automatically calculated as variations from your accent color.

However, as explained in such links as...:

...it is possible to edit the Registry binary value

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Accent\AccentPalette

to set the colors of elements of the Windows taskbar. To summarize the Reddit link and the results of my own experiments, AccentPalette consists of the bytes

GG GG GG 00 HH HH HH 00
JJ JJ JJ 00 KK KK KK 00
LL LL LL 00 MM MM MM 00
NN NN NN 00 PP PP PP 00

These are RGB triplets delimited by a 00 null byte. Each RGB triplet is used by Windows to determine the color of a different element of the interface. These RGB triplets are generally applied to the interface without any modification to their value. For example, LL LL LL is used as the exact background color of the active taskbar button (the button in the taskbar corresponding to the active window), and MM MM MM is used as the exact background color of inactive taskbar buttons (buttons in the taskbar corresponding to inactive windows).

The HH HH HH RGB triplet determines the color of the underline beneath taskbar buttons with an open window. The same underline color is used for all open windows, both active and inactive. That underline is a light blue line in the example images here (re-used from the Superuser questions above):

Taskbar Example Image 1

Taskbar Example Image 2

The difficulty is that unlike the other RGB triplets, Windows 10 does not use this HH HH HH value without modification. If all three color components are greater than or equal to the hex value B3, then Windows uses the triplet as the color of the underline without modification, but if any of the three color components are less than B3, then Windows applies some sort of lightening blend/transformation to the triplet to get the final color.

Let us designate the three color components (red, green, and blue) of this HH HH HH Registry AccentPalette triplet as $S_R$, $S_G$, and $S_B$, and the three color components of the final taskbar underline color as $T_R$, $T_G$, and $T_B$.

My question is, based on the data below, what is the exact function or algorithm $f(S_R, S_G, S_B) = (T_R, T_G, T_B)$ of this blend/transformation? Based on the data below, how exactly does Windows 10 calculate the final taskbar underline RGB color from the HH HH HH triplet?

Here is a table of data I have manually gathered by editing the Registry, restarting Explorer so that Windows 10 will apply the change, taking screenshots, and then using an image editor's eyedropper tool to determine the final resulting color. The values are all in hex. This is overall an extremely tedious process, hence the limited data (and obviously, I can't manually test all 256 x 256 x 256 = 16,777,216 possible RGB triplets).

$S_R$ $S_G$ $S_B$ $T_R$ $T_G$ $T_B$
00 00 00 B3 B3 B3
00 00 01-B3 75 75 B3
00 00 B4 75 75 B4
00 00 B5 76 76 B5
00 00 B6 76 76 B6
00 00 B7 77 77 B7
00 00 B8 93 93 C6
00 00 B9 78 78 B9
00 00 BA 79 79 BA
00 00 BB 7A 7A BB
00 00 C0 7D 7D C0
00 00 C8 82 82 C8
00 00 CC 85 85 CC
00 00 D0 87 87 D0
00 00 D8 8D 8D D8
00 00 DD 90 90 DD
00 00 E0 92 92 E0
00 00 E8 97 97 E8
00 00 F0 9C 9C F0
00 00 F8 A1 A1 F8
00 00 FF A6 A6 FF
00 01 00 75 B3 75
00 01 01 75 B3 B3
00 01 02 75 94 B3
00 01 03 75 89 B3
00 01 04 75 84 B3
00 01 05 91 9A C2
00 01 06 75 7F B3
00 01 07 75 7D B3
00 01 08 75 7C B3
00 01 12-15 75 78 B3
00 01 16-1F 75 77 B3
00 01 20-40 75 76 B3
00 01 41-B3 75 75 B3
00 01 B4 75 76 B4
00 01 FF A6 A6 FF
74 75 76 B0 B1 B3
B0 B1 B2 B1 B2 B3
B1 B1 B1 B3 B3 B3
B1 B2 B3 B1 B2 B3
B1 B3 B4 B1 B3 B4
B2 B3 B4 B2 B3 B4
B3 00 B3 B3 75 B3
C0 D0 F0 C0 D0 F0
FF 00 FF FF A6 FF
FF FF FF FF FF FF

Some observations from my experimentation:

  • As mentioned previously, if all three color components are greater than or equal to the hex value B3, then no change is applied. But if any of the three color components are less than B3, then Windows applies some sort of lightening blend/transformation to the triplet to get the final color.
  • The result is deterministic. The same S values always result in the same T values.
  • The result is independent in the sense that it is not affected by any other triplets in the AccentPalette value.
  • Any permutation applied to the S values results in the same permutation being applied to the T values. For example, if $f(S_1, S_2, S_3) = (T_1, T_2, T_3)$, then $f(S_1, S_3, S_2) = (T_1, T_3, T_2)$. That is, the red, green, and blue components are interchangeable in the sense that any of them can be swapped, and the same components will be swapped in the result.
  • The mapping is many-to-one. One example from the table above is that if $S_R$ = 00 and $S_G$ = 00, then any value of $S_B$ from 01 to B3 will give the result value (75, 75, B3). That is, $f(00, 00, 01-B3) = (75, 75, B3)$.
  • The function might look linear at a glance within some ranges, but a closer examination shows it is not truly linear. For example, look in the table above at the results in the range $f(00, 00, B4-B7)$. Within that range, $T_B$ is simply $S_B$, but for $T_R$ and $T_G$, it increments first from 75 to 76, but then stays at 76 for another step before incrementing again to 77.
  • The function generally is fairly smooth, but it has some wild discontinuities. As seen in the table above, it is smooth-ish in the ranges $f(00, 00, B4-B7)$ and $f(00, 00, B9-BB)$, but $f(00, 00, B8)$ gives a wild jump that is far from the values surrounding it.
  • 4
    This is likely to do better on the comuter grahics stack exchange – FShrike Jul 08 '23 at 19:21
  • 1
    What the hell is up with "00 00 B8"? – Rei Henigman Jul 08 '23 at 19:28
  • @Rei Henigman: Yeah, I know, it's really strange! It was so strange, I checked the value multiple times to make sure it was correct, and then, as I continued gathering data, I realized it was not the only strange value. I specifically called out f(00, 00, B8) in the list of observations below the table as one of several wild discontinuities: there are other strange discontinuities, such as f(00, 01, 05). – WingedKnight Jul 08 '23 at 19:33
  • 1
    @FShrike +1. This is really not a mathematical problem. For all we know, Windows may have the full $256^3$-entries' table and just take values from it. Which could've been generated by some algorithm way back in the past. With the algorithm having bugs, which would explain the oddities. Or maybe it was hand-adjusted. We would never know. –  Jul 08 '23 at 20:00
  • I’m voting to close this question because it has to do with internal details of a software product and has nothing to do with mathematics. – Rob Arthan Jul 10 '23 at 23:01
  • Shortly after I posted this question, I flagged this question to be migrated to the Computer Graphics StackExchange, which I did not previously know existed, but I guess the migration is being delayed due to the mod strike. – WingedKnight Jul 13 '23 at 00:24

1 Answers1

3

First, I agree with Fshrike and Stinking Bishop that math stack exchange is probably not the appropriate forum for posting these kind of questions. Nevertheless, I gave it a shot.

I played a bit with the dataset, and I came up with a simple algorithm which almost works. It fails with very small errors for 8 of the given inputs, and it completely fails for the 2 weird inputs (00 00 B8 and 00 01 05).

  • "test_function" is just a util to compare the result of the function to the given data.

  • "algorithm" is the function that I came up with, that gets 3 RGB values, and calculate 3 new RGB values.

See the provided code:

# pairs is a list where each object is a pair of input and expected output.
# For example: ((0, 1, 7) , (117, 125, 179))

def test_function(f): total_distance_squared = 0 for pair in pairs: res = f(pair[0]) if res != pair[1]: distance_squared = sum([(res[i] - pair[1][i])**2 for i in [0,1,2]]) print(f"input: {pair[0]}, res: {res}, expected_res: {pair[1]}") total_distance_squared += distance_squared

print(f"total error: {total_distance_squared}")
return total_distance_squared

special_val = 0xb3 constant = 0.65084 # quite close to 41/63, but I don't know... def algorithm(triplet): a, b, c = triplet[0], triplet[1], triplet[2] if a == 0 and b == 0 and c == 0: return special_val, special_val, special_val

max_val = max(a,b,c)
if max_val < special_val:
    r = special_val/max_val
    a, b, c = (round(a*r), round(b*r), round(c*r))

new_max_val = max(a, b, c)
if a < special_val:
    a = round(constant * new_max_val + (1-constant) * a)
if b < special_val:
    b = round(constant * new_max_val + (1-constant) * b)
if c < special_val:
    c = round(constant * new_max_val + (1-constant) * c)

return a, b, c

test_function(algorithm)

Running this code with your data (excluding the two weird inputs) prints:

input: (0, 1, 7), res: (117, 126, 179), expected_res: (117, 125, 179)
input: (0, 1, 32), res: (117, 119, 179), expected_res: (117, 118, 179)
input: (0, 1, 65), res: (117, 118, 179), expected_res: (117, 117, 179)
input: (116, 117, 118), res: (178, 178, 179), expected_res: (176, 177, 179)
input: (176, 177, 178), res: (178, 179, 179), expected_res: (177, 178, 179)
input: (177, 178, 179), res: (178, 179, 179), expected_res: (177, 178, 179)
input: (177, 179, 180), res: (179, 179, 180), expected_res: (177, 179, 180)
input: (178, 179, 180), res: (179, 179, 180), expected_res: (178, 179, 180)
total error: 17

To try and explain the steps of the algorithm in words:

  • If all values are $0$, return 0xB3, 0xB3, 0xB3.

  • If all values are smaller than 0xB3, normalize all of the values with the same constant such that the largest value is 0xB3.

  • Now, for every value $v$, if it's smaller than 0xB3, replace it with: $0.65084 \cdot M + (1-0.65084)\cdot v$, where $M$ is the maximum of the three numbers.

A couple of comments:

  • I have no explanation for the 2 weird inputs.
  • I believe that the small errors are some kind of rounding errors (I couldn't find a constant that would perfectly work).
  • As mentioned in the code, the value of the magic constant is very close to $\frac{41}{63}$ but I don't know if it's just by chance.
  • A bit unrelated but still - If for some reason you want a very accurate solution, that would match your data on the nose, I think it would make more sense to attack this problem with reverse engineering of the code of Windows.
Rei Henigman
  • 1,394
  • This is way off-topic for MSE. – Rob Arthan Jul 08 '23 at 21:38
  • 1
    I believe that it answers the question that the OP asked, which is currently not flagged to be closed. My reasoning is that as long as this question is kept in MSE, it makes sense to post answers to it. Meta question - would it be better if I didn't post this answer? – Rei Henigman Jul 08 '23 at 21:43
  • It would have been far better to have advised the OP to post the question on a relevant forum. – Rob Arthan Jul 10 '23 at 23:00