fluttercolorsbackgroundbrightnessluminance

How to check brightness of a background color to decide text color written on it


I have a simple question, but I wasn't able to find an answer to this. Note, that I'm almost a complete beginner.

So I have an app (it's not mine, but I'm contributing to it), and in there is writing on a color background, which can be changed by the user. The writing should appear black if the background is bright enough but stay white if it isn't.

The application is a school diary app for elementary and high-school students that connects to the state-wide school diary service in Hungary. Here, the best note is 5, the worst is 1. The user can set the colors of each grade in the settings. Right now, only the code for the note "4" is hard-coded to have black text (because the background is yellow by default on "4" notes), all others have white. This is what I want to automate.

Example of white text

Example of black text

This is the main screen of the app for reference

Page where user can change color for a kind of note

Code right now:

switch (evaluation.NumberValue) {
    case 1:
      bColor = globals.color1;
      fColor = Colors.white;
      break;
    case 2:
      bColor = globals.color2;
      fColor = Colors.white;
      break;
    case 3:
      bColor = globals.color3;
      fColor = Colors.white;
      break;
    case 4:
      bColor = globals.color4;
      fColor = Colors.black; //This should be white if color4 is dark enough. Same applies to all of them.
      break;
    case 5: //I'm looking for something like this:
      bColor = globals.color5;
      fColor = (lightLevel(globals.color5) > 50) ? Colors.black : Colors.white;
      break;
    default:
      bColor = Colors.black;
      fColor = Colors.white;
      break;
  }

I'm looking for something like this:

 case 5: //I'm looking for something like this:
  bColor = globals.color5;
  fColor = (lightLevel(globals.color5) > 50) ? Colors.black : Colors.white;
  break;

Thank you for any help!


Solution

  • Short Answer

    I've written about this in the past here, also on GitHub, with some interactive experiments.

    This question is asked often enough that I recently setup a repo for a lean and mean font color flipper called Max Contrast

    Longer Answer

    To encapsulate previous answers:

    1. Luminance contrast is what is needed for reading, therefore the colors used in a design must be converted to an achromatic luminance using the weighted values for each of the red, green, blue monitor primaries.

    2. Using perceptually uniform contrast math such as APCA, you can determine the text contrast-center point between white and black.

      • The center is actually a small range where either black or white may have an advantage depending on the context of use, environment, and other factors.
    3. Using standard luminance calculations, an ideal center for SDR sRGB displays is about 36 Y. The range is about 34Y to 42Y, depending on the factors.

    4. Another factor for the center area, is the need for a larger font than the higher contrast outer regions. The range is about 22Y thru 53Y, ish, or negligible if already using a substantial font. This is demonstrated at the "Flip for Color" demo page.

    5. The essential snippet from Max Contrast:

        // Simple: send it sRGB values 0-255.
       // If Ys is > 0.342 it returns 'black' otherwise returns 'white'
      // See  https://github.com/Myndex/max-contrast 
     // trc = gamma, Rco = red coefficient, Rs = Red signal
    // Ys = estimated screen luminance (APCA 0.0.98G4g)
    
    function maxContrast (Rs = 164, Gs = 164, Bs = 164) {
      
      const flipYs = 0.342; // based on APCA™ 0.98G middle contrast BG
      const trc = 2.4, Rco = 0.2126729, Gco = 0.7151522, Bco = 0.0721750; // 0.98G
      let Ys = (Rs/255.0)**trc*Rco + (Gs/255.0)**trc*Gco + (Bs/255.0)**trc*Bco; 
    
      return Ys < flipYs ? 'white' : 'black'
    }
    

    Note: This snippet uses APCA compatible constants. The flip value is set to YS 34.2 which is APCA estimated screen luminance, optimized for self illuminated displays, and slightly different from CIE luminance (Y) calculations. The "font range" where larger fonts are needed is YS 20 to YS 50.