It's not always a valid expectation that the OS will allow you to do what it itself does. Just because "the os should be able to access the screen" doesn't mean that you (in userland) should be able to do that. And most certainly, under Windows you have no unfettered screen access, not least because of DRM: there is a protected media path (PMP) for protected video and audio content, and you won't be able to sniff it from an arbitrary process.
So: whatever night light you may implement by accessing the screen contents, won't work as soon as any protected content is displayed, e.g. from Netflix, Hulu, Youtube Red, system DVD player, BluRay player, etc. To get around PMP you'd need to write a driver, sign it using an EV code signing certificate, and then submit it for automated attestation signing by Microsoft. And you'd not dare put it on the web, because Microsoft would blacklist that driver (i.e. its presence in the kernel would cause PMP-using applications to refuse to play protected content), and perhaps quickly at that.
With that out of the way, there are several ways that Windows could implement it, but we don't care because we're not writing a direct replacement for the Windows Night Light component. All we have access to is userland. Here's what we got to work with, then:
Windows itself performs monitor colorspace calibration using the "gamma ramp" system: a lookup table, historically used mostly for linearization of the monitor response, and subsequently used as a kitchen sink. There are three generations of those APIs:
The most legacy-compatible way would be to use the Windows Color System APIs, supported since Windows 2000. The function to use is SetDeviceGammaRamp
. There is a caveat: Windows doesn't allow compressing the color too much, and it's quite arbitrary about what it allows. This family of APIs may fail if you try to do "too much".
Another highly compatible API would be within Direct3D 9 (available since Windows 98 and XP). It offers SetGammaRamp
, with same data table format as that of SetDeviceGammaRamp
above.
Yet another highly compatible API would be within Direct3D 10-12's DirectX Graphics Interface - DXGI, specifically IDXGIOutput::SetGammaControl
. This one was available since Windows Vista, and thus is available on currently (2020) supported Windows versions.
The next most compatible approach would be to use the Magnification API.
The last approach, working only on your own application's window, would be to apply a shader to your window.