Real GBA and DS-Phat colors

@Brankale Thanks for telling me about all of this. I’m glad to see you work with color matrix with proper conversions shown on your github. Now to answer your questions.

It seems there isn’t a full colorspace conversion pipeline (1) (RGB → linear RGB → XYZ → chromatic adaptation → XYZ → linear RGB → RGB). I saw matrix multiplications in the shaders suggesting RGB→XYZ conversion, but without documentation I’m unsure how recent measurements and conversions were handled, especially since tools and methods have changed over time.

Yeah that was long before any color shaders that relies on color matrix and chromatic adaptions has existed, such as Colorimetry, Grade, and Chromaticity from the “misc” folder inside the shader folders. So we relied on just gamma corrected to linear, handling color matrix manually by modifying RGB individually.

But as I recently got back and have multiple host colorspace such as DCI-P3, AdobeRGB, and Rec2020, I was thinking of switching from color-mangler to something like Colorimetry as it contains multiple targeted specified colorspaces. I did play around a bit with Colorimetry shader and added gamma correction to have linear behavior on the color matrix as it doesn’t have one originally. I did modify its XYZ for its host colorspace just to copy the given colors to my current shaders. I can add multiple host colorspace and targeted colorspace to the shader, as well as white balance.

But yeah, with the color mangler, I used it to give a much better understanding how the shaders work with modifying RGB channels as well as easier read to port it to their projects, being mGBA, PPSSPP, and even Gameboy Interface. It’s been ported multiple times, seeing a same similar code that I can easily adjust for just one colorspace for one standard.

It looks like the shaders assume a fixed gamma across RGB channels which is very problematic given how unstable gamma curves are on old consoles. In fact, even a small gamma deviation (e.g., 0.15) can cause DeltaE > 3 (5) , which is significant when aiming for accuracy. This issue is one of the reasons I paused my work after reading DisplayMate’s article on the DS Lite, which only included the grayscale gamma curve.

I used more of an average outside of GBC as I see the average of gamma of the display that is suitable for the viewer. Much of them seem to follow closely to Pure Power Gamma curve as I do now work with a display to Gamma 2.2 since KDE Wayland only color corrects to sRGB with Gamma 2.2 curve. I know a lot of those old TN displays have colder greyscales than how the shaders gamma handles it. It was why I had the handheld presets use the LUT shader to load up its greyscale gamma curves that changes its color temperature. I noticed the DS-Lite has colder greyscale than GBA-SP-101. Even the PSP has a really cold greyscales. But yeah, I did have the LUTs for my current version to contain the cold ones for each system, although made under sRGB curve, which I plan to change to just shader based gamma correction with colder greyscales. Also the reason I didn’t have the generic RGB gamma was the fact it mostly covers just the middle values rather than spreading it out to most of the greyscale. By the looks at your code, it seems to do 9 greyscales between black and white, which can help out. The current shader I have just specifies the gamma for the shader ports to use, as well as the fact the color-mangler doesn’t have RGB gamma control.

As for GBC, it uses its own abnormal gamma curve. I used my ColorMunki Photo with the SpotRead app to just scan luminance the screen while swapping the greyscale swatches from 240p test suite. I scanned all 32 swatches to produce a much more accurate as to how my GBC displays its own gamma to provide constant details. It also has its colder temps on the greyscale too, which I plan to add in later on.

Could you please share the HCFR CHC output files for the consoles you measured?

Sure. Once I get on, I’ll be sharing the data I got for GBA-SP-101, NDS Phat top and bottom, DS-Lite top and bottom, GB Micro, and the PSP 1000. As for GBC, GBA, and GBA-SP-001, they’re pretty much eyeballed on the colorspace with bunch of tools to use, and gamma seems closely identical to Pure Power Gamma, except just slightly darker on very dark swatches only. I did scan out the gamma, but I had to stabilize them in GIMP to see the best results since the colormunki photo had to scan at the very dark that has a slight noise variation to very dark luminance, and same with GBC. So I only have CHC data for any screens covered here that are emitted rather than refletive. As for the Switch OLED, it already has its HCFR data shown from a video I shared here last year or two.

So yeah, I see your code being more promising to have more options to target colorspace without having a lot of work to manually put in color numbers for each RGB for any four hosts colorspace.

2 Likes

Here is the HCFR data that I sampled for the aforementioned handheld consoles from sampling with Colormunki Photo.

https://mega.nz/file/nVxykDLQ#GQNsFMV9yCVhIn3qI4XzpOika3mU6hTDzXJ6zrLF8ys

Also to bring up, I decided to load up gamma related shaders as a separate pass before it color corrects. Not only because of my lack of knowledge of coding shaders from scratch, but also needed to have the gamma pass set before any LCD shaders get applied, as I describe a couple of replies ago on how the LCD shaders should work when using the color correction. Otherwise, the LCD shaders would be less faithful to where a bright red color for example wouldn’t look bright red on the LCD if the color correction gets applied before the LCD shader. Having it applied after would make it look more faithful to the screen, as the subpixels are already using different color gamut itself. So pretty much, gamma shader pass -> LCD shader pass -> color correction pass.

Another thing to add to the current shaders is I lowered the luminance to show off the actual yellow colors since the red parts are clipped. The fact the blue color has to be subtracted because the blue color is out of gamut for sRGB for example would need the white color to be balanced, which means the red color from red and green channels adds up beyond 1.0 or 255, and then the blue color subtracts it back to 1.0. The yellow already has high red colors and it looks less warm and less accurate to the screen if it’s clipped.

1 Like

@Pokefan531 Thanks a lot for the quick response.

I just want to clarify this a little bit:

By the looks at your code, it seems to do 9 greyscales between black and white

In my shader code and in general for my colorspace conversions, I use/need the exact gamma curve values for each R, G and B channels. Using only the greyscale gamma curve is not enough and very far from getting accurate results.

So at the end, you need to measure:

  • at least 9 patches for every channel (9*3 = 27 patches) between 10%-90% (10% increment).
  • 4 patches for every channel near black (4*3 = 12 patches) between 1%-4% (1% increment) (in the shader code I currently use only 4% to make calculations easier and also because there’s not a noticeable difference)
  • 4 patches for every channel near white (4*3 = 12 patches) between 96%-99% (1% increment) (in the shader code I use only 96% since deltaE error is very low on high luminance values)

These are only general rules to get a very good approximation without wasting too much time but obviously if you want to reach the highest level of accuracy you can also measure the exact gamma for every possible color of every channel but, keep in mind the screen bit depth to avoid unnecessary measurements. For example, DS Lite has a bit depth of 6 bits which means 64 color patches for every channel (64*3 = 192 color patches).

Also in the “measurement” readme I wrote, you can find all the data needed by the shaders and colorspace conversions: https://github.com/Brankale/Nintendo-DS-Lite-colorspace/tree/main/measurements.

I’m also trying to make a measurement report template to give more context on the measurements. This is one example https://github.com/Brankale/Nintendo-DS-Lite-colorspace/blob/main/measurements/New%203DS%20XL/20241221_top_pica200/REPORT.md. If you want you can follow this one.


@pica200 I’d like to take this opportunity to ask if you could provide us with the HCFR CHC files for both the DSi and DSi XL, since you mentioned them in a previous post. This way, we can collect data for almost all consoles in the NDS family.

Thanks again

@Pokefan531 I think I now understand what did you meant by

By the looks at your code, it seems to do 9 greyscales between black and white

If you measure the greyscale you already get the gamma of Red, Green and Blue because grey already has all of them. I don’t know how I missed that.

This also invalidates partially what I said in the previous message. There’s no need to measure all those color patches. You just need to measure 9 patches as you said (10-90%) and a few other patches for near black and near white. So for example for DS Lite they are only 64 patches if you want the max accuracy and not 192.

From the greyscale patches, I was curious on how accurate the patches would be because of the limited color depth the hardware has, GBA being 5 bit, and DS being 6 bit from your research. Tho I thought it has 8 bit when I used moonshell long time ago, but must be dithered. I get more promising results in GBA’s 240p test suite than when tried from moonshell. That’s unless if there is a similar homebrew made for NDS hardware that uses more color for better color sampling.

Yeah I get that only 9 patches is a solid choice to save time and such. I do worry on the accuracy on the gamma for GBA and NDS because of limited color depth where you can’t exactly match the color RGB value from the screen HCFR expects for each patches. It was why I did 32 patches for those screens to collect full data as none of the homebrew could dither through currently. As well as the issue also applies to black and white four patches since the GBA for example have a limited color depth that makes each BW patches have bigger luminance difference between each other, even for just one notch difference, compared to other displays that allows full 8bit color display where there’s more subtle difference between patches on luminance.

I can successfully do the PSP for 9 patches and 4 for black and white since it does allow 8bit mode for RGB and uses it for its interface when I load pictures from it. The GBA and NDS with more limited depth options is a different story. 10% grey for example expects 26 RGB to be show from the display that HCFR gives out, but GBA would only have 24 or 31 RGB greys due to limited depth, which may affect gamma results. As for the near black or near white, it would be 0 RGB, then 8 RGB, or 255 RGB, then 247 RGB, respectively. It was why I only put 32 black to white patches to the data as I wanted to preserve the gamma results without any mismatch to my concerns.

As for CHC data for DSi family, I sadly don’t have them as I only have the Phat and Lite models when I mentioned that I would share them. As for the instructional measurement, I do have Colormunki Display and Colormunki Photo, a Colorimeter and Spectrometer, respectively. I used Photo for Display’s correction for just my monitor when calibrating for better whitepoints. I wasn’t sure if the Photo is less accurate when reading your report files, but I did get very similar points from how my Display samples the colors. The Photo was able to read black colors with hardly any struggles because of its TN panels having less contrast where the black color isn’t too dark on full brightness and just around 600:1 from DS Lite.

Yeah I did my best to explain all this just to help out, I do apologize if there were few things that didn’t make sense, but I try to understand all of them as best as I can. I was happy to see your glsl shaders on the repository for each screen. My fun fact, I measure my PC monitor with higher than 10 patches on HCFR, like 20, just to check the gamma curve for the dark areas to say it follows closely to pure power gamma, as well as measuring near black and white levels to see more deeper analysis.