【问题标题】:How do you find the color contrast using APCA (Advanced Perpetual Contrast Algorithm)?如何使用 APCA(高级永久对比度算法)找到颜色对比度?
【发布时间】:2021-06-08 13:16:03
【问题描述】:

所以我想为给定的foregroundRGBAbackgroundRGBA 颜色对编写一个颜色对比度计算器,使用Advanced Perpetual Contrast Algorithm (APCA)(阅读Google WebDev's mini-article on APCA)。

虽然上述网站有一些代码示例可以做同样的事情,但它们在任何一种颜色具有透明度时无效 (0 ≤ α

点数1 是这里最大的问题,因为前景色和/或背景色可能是半透明的。在这种情况下会出现问题。我真的无能为力。例如,该站点上的 RGBColor.toY 函数仅适用于 sRGB 颜色,不适用于具有 alpha 透明度的 RGB 颜色。

这是我需要帮助的地方。

如何使用 APCA 为给定的一对 foregroundRGBAbackgroundRGBA RGBA 颜色对象编写颜色对比度计算器?

我复制了站点中使用的常量,并为一些函数编写了一些代码,以及RGBA颜色类(其对象将作为参数传递给即将编写的APCA颜色对比度计算器,它可以使用RGBA 对象及其属性来简化计算和提高可读性)。

网站中给出和使用的常量

const sRGBtrc = 2.4;    // Gamma for sRGB linearization. 2.223 could be used instead
                        // 2.218 sets unity with the piecewise sRGB at #777

const Rco = 0.2126;     // sRGB Red Coefficient
const Gco = 0.7156;     // sRGB Green Coefficient
const Bco = 0.0722;     // sRGB Blue Coefficient

const scaleBoW = 1.14;  // Scaling for dark text on light z
const scaleWoB = 1.14;  // Scaling for light text on dark — same as BoW, but
                        // this is separate for possible future use.
const scaleOffset = 0.027;  // Offset

const normBGExp = 0.56;     // Constants for Power Curve Exponents.
const normTXTExp = 0.57;    // One pair for normal text,and one for REVERSE
const revBGExp = 0.62;      // FUTURE: These will eventually be dynamic
const revTXTExp = 0.65;     // as a function of light adaptation and context

const blkThrs = 0.022;  // Level that triggers the soft black clamp
const blkClmp = 1.414;  // Exponent for the soft black clamp curve

杂项。功能

// Clamps a value between a given range [minimum, maximum]
const clamp = (value, minimum=0, maximum=1) => {
  if (value < minimum) return minimum;
  if (value > maximum) return maximum;
  return value;
}

// Modified clamp to clamp an RGBA color value in [0, 255]
const clampColor = (value=0) => clamp(Math.round(value), 0, 255);

RGBA 颜色类

// An RGBA color class to be used by `foregroundRGBA` and `backgroundRGBA`
class RGBA {
  constructor(red=0, green=0, blue=0, alpha=1) {
    // Clamp the given r, g, b arguments between 0 and 255
    this._red = clampColor(red);
    this._green = clampColor(green);
    this._blue = clampColor(blue);

    // Clamp a between 0 and 1 since it is alpha
    this._alpha = clamp(alpha);
  }

  get r() {
    return this._red;
  }

  set r(value) {
    this._red = clampColor(value);
  }

  get linearizedR() {
    return (this._red / 255) ^ sRGBtrc;
  }

  get g() {
    return this._green;
  }

  set g(value) {
    this._green = clampColor(value);
  }

  get linearizedG() {
    return (this._green / 255) ^ sRGBtrc;
  }

  get b() {
    return this._blue;
  }

  set b(value) {
    this._blue = clampColor(value);
  }

  get linearizedB() {
    return (this._blue / 255) ^ sRGBtrc;
  }

  get a() {
    return this._alpha;
  }

  set a(value) {
    this._alpha = clamp(value);
  }

  // Not given in the site (since the site is using RGB and not RGBA)
  // Written based on assumption (since this._alpha is already betwen 0 and 1 unlike the others)
  get linearizedA() {
    return this._alpha ^ sRGBtrc;
  }

  // Relative Luminance (Y) property of a given RGBA color
  // Works only for RGB and not RGBA (since the formula makes no use of this._alpha and purely returns the value based on RGB and not RGBA)
  get Y() {
    return (
      Math.pow(this._red/255, sRGBtrc) * Rco
    + Math.pow(this._green/255, sRGBtrc) * Gco
    + Math.pow(this._blue/255, sRGBtrc) * Bco

    // Assumption again
    // Also, if there exists an `Aco`, its value is unknown (part of my question)
    // + Math.pow(this._alpha, sRGBtrc) * Aco
    );
  }

  // Modified `toString` to return a CSS-compatible RGBA color
  toString() {
    return `rgba(${this._red},${this._green},${this._blue},${this._alpha})`;
  }
}

要编写的基于 APCA 的颜色对比度计算器

const getAPCAColorContrast = (foregroundRGBA=new RGBA(), backgroundRGBA=new RGBA(255, 255, 255)) => {
  // Code to be written according to the algorithm using passed RGBA objects and their properties
  // WHICH MUST BE COMPATIBLE WITH RGBA COLORS AND NOT JUST RGB COLORS

  return colorContrast;  // Calculated APCA-based color contrast
}

我尝试寻找可以在任何地方计算 RGBA 颜色的 APCA,但失败了。

我会尽力回答您的任何问题,以帮助您给出更好的答案。
如果我需要进行任何修改以提出更好的问题,请告诉我。

如果您能找到一种使用 APCA 计算 RGBA(不仅仅是 RGB)颜色对比度的方法,我将不胜感激。

编辑 1:请注意,我不需要代码来填充函数 (getAPCAColorContrast)。我需要的只是一个 APCA 算法,它不仅兼容 RGB,还兼容 RGBA。一旦我知道并理解了用于 RGBA 前景色和背景色的修改后的 APCA 算法,我就可以自己编写代码。 (此免责声明已写在这里,以便问题不会因违反规则而被删除,如果有的话)。

【问题讨论】:

  • 您可能希望在 GitHub 存储库中提交问题以获取 Web 内容可访问性指南:github.com/w3c/wcag/issues
  • @PeterO。我正打算这样做。但我不确定我是否应该这样做。现在我会这样做。谢谢你告诉我!
  • 请注意,此问题中指向 APCA 的链接已过时。请参阅规范的 GitHub 存储库:github.com/Myndex/SAPC-APCA

标签: javascript algorithm colors rgba contrast


【解决方案1】:

如有任何混淆,我们深表歉意。 Markov00 的回答基本上是正确的,但由于我是 APCA 背后的人,我认为我应该提供一个“官方”的答案。我今天偶然发现了这个问题。

首先,对代码的状态感到抱歉 - 这是非常早期的测试版,我的重点一直是支持研究,而不是代码。

说实话,我没想到会引起这么多的兴趣以及早期采用者的数量。所以,我正在开发一个更完整的版本,并且我确实计划在今年年底之前有一个清理版本和 npm 项目。

资源

APCA 有一个 GitHib 存储库,我通常会在一天内回复那里的问题。 https://github.com/Myndex/SAPC-APCA

我希望尽快有最新的代码和文档。 “前沿”是https://www.myndex.com/SAPC/的SAPC研究工具

我确实看到您从 W3 GitHub 获得了代码,不幸的是,这是一个非常早期的草稿,通常不应该使用,因为常量不同并且会产生非常不同的结果。

2021 年 10 月 1 日的当前版本是 APCA 0.98 G-4g(关键是 G-4g 常数,因为它们决定了结果。)

关于透明度的使用

APCA 函数需要看到颜色,因为它们将被渲染到屏幕上。作为算法的一部分,它不具有透明度,因为所有合成或混合操作必须在 APCA 处理之前在给定的色彩空间中完成。

透明度或混合与 APCA 无关,这是由用户代理和 CSS 规则定义的,并且有许多不同的方式来合成或处理透明度。在不知道多层颜色或图像将混合或光栅化到屏幕上的方式的情况下,不可能预测最终的 sRGB 值。

例如,CSS4 颜色将添加线性和其他颜色空间,以及新的渐变和其他颜色工具。最终,重要的是颜色将如何出现在屏幕上,因此留给用户代理/浏览器/应用程序进行混合/合成,然后将生成的 sRGB 值发送到 APCA 函数。

并且重要的是,将背景和刺激(文本)发送到正确的文本和 APCA 的 BG 输入,因为 APCA 取决于极性。

阿尔法

也就是说,颜色对象实际上有 alpha 输入,并且还有很多待发布的功能供未来发布。正如我所提到的,有些人正在期待 CSS4 颜色规范。

关于 alpha:alpha 没有 gamma,但 A 通道可能需要根据所需结果进行调整。但通常(在 CSS 4 之前),浏览器不会在混合操作之前对颜色进行线性化,因此必须首先在正确的颜色空间中完成(即浏览器将使用相同的颜色空间)。

然后您可以计算正确的 RGB 值来计算对比度。但是您需要的是渲染到屏幕上的颜色,这意味着您必须先完成与底层颜色的合成。

如果您不知道基础颜色,则无法计算对比度,因为它是模棱两可和未定义的。

G-4G 常数

为方便起见,这里是最简单的代码版本,没有颜色对象的额外内容,只是获取 sRGB 文本颜色和 sRGB BG 颜色,将它们转换为亮度(Y),然后将对比度值作为数字返回Lc

///////////////////////////////////////////////////////////////////////////////
/////
/////    APCA - Advanced Perceptual Contrast Algorithm - Beta 0.98G-4g
/////     
/////    Function to parse color values and determine SAPC/APCA contrast
/////    Copyright © 2019-2021 by Andrew Somers. All Rights Reserved.
/////    LICENSE: APCA version to be licensed under W3 cooperative agrmnt.
/////    CONTACT: For SAPC/APCA Please use the ISSUES tab at:
/////    https://github.com/Myndex/SAPC-APCA/
/////
///////////////////////////////////////////////////////////////////////////////
/////
/////    USAGE:
/////        Use sRGBtoY(color) to convert sRGB to Luminance (Y)
/////        Then send Y-text and Y-background to APCAcontrast(Text, BG)
/////
/////    Lc = APCAcontrast( sRGBtoY(TEXTcolor) , sRGBtoY(BACKGNDcolor) );
/////
/////    Live Demonstrator at https://www.myndex.com/APCA/
/////
///////////////////////////////////////////////////////////////////////////////


//////////   APCA G - 4g Constants   //////////////////////////////////////


const mainTRC = 2.4; // 2.4 exponent emulates actual monitor perception
    
const sRco = 0.2126729, 
      sGco = 0.7151522, 
      sBco = 0.0721750; // sRGB coefficients

const normBG = 0.56, 
      normTXT = 0.57,
      revTXT = 0.62,
      revBG = 0.65;  // G-4g constants for use with 2.4 exponent

const blkThrs = 0.022,
      blkClmp = 1.414, 
      scaleBoW = 1.14,
      scaleWoB = 1.14,
      loBoWthresh = loWoBthresh = 0.035991,
      loBoWfactor = loWoBfactor = 27.7847239587675,
      loBoWoffset = loWoBoffset = 0.027,
      loClip = 0.001,
      deltaYmin = 0.0005;


////////// ƒ sRGBtoY()   ///////////////////////////////////////////////

function sRGBtoY (sRGBcolor) {
                  // send 8 bit-per-channel integer sRGB (0xFFFFFF)

  let r = (sRGBcolor & 0xFF0000) >> 16,
      g = (sRGBcolor & 0x00FF00) >> 8,
      b = (sRGBcolor & 0x0000FF);
    
  function simpleExp (chan) { return Math.pow(chan/255.0, mainTRC); }
 
         // linearize r, g, or b then apply coefficients
        // and sum then return the resulting luminance
    
   return sRco * simpleExp(r) + sGco * simpleExp(g) + sBco * simpleExp(b);
}


////////// ƒ APCAcontrast()   //////////////////////////////////////////

function APCAcontrast (txtY,bgY) {
                         // send linear Y (luminance) for text and background.
                        // IMPORTANT: Do not swap, polarity is important.
        
  var SAPC = 0.0;            // For raw SAPC values
  var outputContrast = 0.0; // For weighted final values
  
  // TUTORIAL
  
  // Use Y for text and BG, and soft clamp black,
  // return 0 for very close luminances, determine
  // polarity, and calculate SAPC raw contrast
  // Then scale for easy to remember levels.

  // Note that reverse contrast (white text on black)
  // intentionally returns a negative number
  // Proper polarity is important!

//////////   BLACK SOFT CLAMP   ///////////////////////////////////////////

          // Soft clamps Y for either color if it is near black.
  txtY = (txtY > blkThrs) ? txtY :
                            txtY + Math.pow(blkThrs - txtY, blkClmp);
  bgY = (bgY > blkThrs) ? bgY :
                          bgY + Math.pow(blkThrs - bgY, blkClmp);

       ///// Return 0 Early for extremely low ∆Y
  if ( Math.abs(bgY - txtY) < deltaYmin ) { return 0.0; }


//////////   APCA/SAPC CONTRAST   /////////////////////////////////////////

  if ( bgY > txtY ) {  // For normal polarity, black text on white (BoW)

           // Calculate the SAPC contrast value and scale
      
    SAPC = ( Math.pow(bgY, normBG) - Math.pow(txtY, normTXT) ) * scaleBoW;

            // Low Contrast smooth rollout to prevent polarity reversal
           // and also a low-clip for very low contrasts
    outputContrast = (SAPC < loClip) ? 0.0 :
                     (SAPC < loBoWthresh) ?
                      SAPC - SAPC * loBoWfactor * loBoWoffset :
                      SAPC - loBoWoffset;

  } else {  // For reverse polarity, light text on dark (WoB)
           // WoB should always return negative value.

    SAPC = ( Math.pow(bgY, revBG) - Math.pow(txtY, revTXT) ) * scaleWoB;

    outputContrast = (SAPC > -loClip) ? 0.0 :
                     (SAPC > -loWoBthresh) ?
                      SAPC - SAPC * loWoBfactor * loWoBoffset :
                      SAPC + loWoBoffset;
  }

         // return Lc (lightness contrast) as a signed numeric value 
        // It is permissible to round to the nearest whole number.
       
  return  outputContrast * 100.0;
}

////////////////////////////////////////////////////////////////////////////////
/////
/////                 SAPC Method and APCA Algorithm
/////
/////   Thanks To: 
/////   • This project references the research and work of Dr.Legge, Dr.Arditi,
/////     Dr.Lovie-Kitchin, M.Fairchild, R.Hunt, M.Stone, Dr.Poynton, L.Arend, &
/////     many others — see refs at https://www.myndex.com/WEB/WCAG_CE17polarity
/////   • Stoyan Stefanov for his input parsing idea, Twitter @stoyanstefanov
/////   • Bruce Bailey of USAccessBoard for his encouragement, ideas, & feedback
/////   • Chris Loiselle of Oracle for getting us back on track in a pandemic
/////
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/////
/////   *****  SAPC BLOCK  *****
/////
/////   For Evaluations, this is referred to as: SAPC-8, 0.98 G-series constants
/////                S-LUV Advanced Perceptual Contrast
/////   Copyright © 2019-2021 by Andrew Somers. All Rights Reserved.
/////   SIMPLE VERSION — Only the basic APCA contrast predictor.
/////
/////   Included Extensions & Model Features in this file:
/////       • SAPC-8 Core Contrast (Base APCA) 
/////       • G series constants, group "G-4g" using a 2.4 monitor exponent
/////       • sRGB to Y, parses numeric sRGB color to luminance
/////       • SmoothScale™ scaling technique (non-clinical use only)
/////       • SoftToe black level soft clamp and flare compensation.
/////
/////
////////////////////////////////////////////////////////////////////////////////
/////
/////                DISCLAIMER AND LIMITATIONS OF USE
/////     APCA is an embodiment of certain suprathreshold contrast
/////     prediction technologies and it is licensed to the W3 on a
/////     limited basis for use in certain specific accessibility
/////     guidelines for web content only. APCA may be used for 
/////     predicting colors for web content use without royalty.
/////
/////     However, Any such license excludes other use cases
/////     not related to web content. Prohibited uses include
/////     medical, clinical evaluation, human safety related,
/////     aerospace, transportation, military applications, 
/////     and uses which are not specific to web based content
/////     presented on self-illuminated displays or devices.
/////
/////
////////////////////////////////////////////////////////////////////////////////

查找表

上面的代码只是为了确定一对颜色的对比度。有用于确定适当的最小字体大小的查找表。最简单的实现请参见https://www.myndex.com/APCA/

如果您有任何问题,请告诉我。

谢谢,

安迪

【讨论】:

    【解决方案2】:

    一种可能的解决方法是首先将提供的 RGBA 背景与实际的不透明背景混合(例如,如果您的背景代表按钮的颜色并且是半透明的蓝色,则必须将该颜色与后面的实际背景混合按钮,如页面颜色)。 混合颜色应该基本上代表感知到的不透明颜色。 前景也需要做同样的事情,将其与感知的背景混合。 然后,您可以应用 APCA 算法来计算对比度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-07
      • 2011-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      相关资源
      最近更新 更多