Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

有趣的 CSS Alpha 值 #7

Open
yisibl opened this issue Apr 24, 2015 · 11 comments
Open

有趣的 CSS Alpha 值 #7

yisibl opened this issue Apr 24, 2015 · 11 comments

Comments

@yisibl
Copy link
Owner

yisibl commented Apr 24, 2015

在 Chrome 中输入 「background-color: rgba(0, 0, 0, 0.5)」

最终渲染出来你会发现变成了 「background-color: rgba(0, 0, 0, 0.498039)」。

Gif demo

阿西!这是什么鬼?( ・᷄ὢ・᷅ ) ,别急下面慢慢揭开它的神秘面纱。

一、 故事背景

在与 @Justineo 聊到 CSS Alpha 百分比表示方式的时候,偶然发现 Chrome 里他的这个项目kolor 的背景色实际计算出来的 Alpha 值和指定的不一样。

这引起了我们的兴趣,一番探讨搜索后,做个简要总结,备忘。

二、概念

CSS Color 中阿尔法通道(α Channel 或 Alpha Channel)是指颜色的不透明度,而不是透明度。也就是说:

  • 不透明度=0 的时候是全透明。
  • 不透明度=1 的时候就是完全不透明。

其中 32 位的颜色被分解成四个8位整数:

  • R: 8位
  • G: 8位
  • B: 8位
  • A: 8位

所以 Alpha 值在内部表示为介于0和255之间的一个整数(即8位无符号整数)。为了更加方便开发者使用,书写的时候我们采用 0-1 之间的浮点数来表示 Alpha 值。

三、 探索

那么我们看看 Chrome 中是如何计算出 0.5 的 Alpha 值的呢?

我们用input来表示输入 CSS 值中解析出来的字符串,A代表最终的 Alpha 值,alphaString来表示 CSS Color 中 Alpha 值的序列化结果:

1 把 0.5 转换为8位无符号整数

 A = atof(input) * 255; // atof() 相当于 JS 函数 parseFloat()

 => A = 0.5 * 255 = 127.5 => 127 // 舍去非整数部分

2 alphaString 通过以下定义来计算

const char* alphaString = numberToFixedPrecisionString(alpha() / 255.0f, 6, buffer, true);

注意:早期版本的 WebKit 采用 16 位来存储 Alpha 值,这会导致和现在版本最终计算出来存在差异,参见:http://www.zhihu.com/question/29781299/answer/45617540

这里alpha()的结果就是 A

alpha() / 255 = 127 / 255 = 0.498039216

然后 CSS Object Model (CSSOM) 规范中的 <number> 值规定最多取 6 位小数,最终反向序列化成字符串,得到你看到的 0.498039

<number>
A base-ten number using digits 0-9 (U+0030 to U+0039) in the shortest form possible, using "." to separate decimals (if any), rounding the value if necessary to not produce more than 6 decimals, preceded by "-" (U+002D) if it is negative.

这里 WebKit 偷懒了啊:

CSSOM 规范里的算法是这样说的:

<alphavalue> 
If the value is internally represented as an integer between 0 and 255 inclusive (i.e. 8-bit unsigned integer), follow these steps:

  1. Let alpha be the given integer.
  2. If there exists an integer between 0 and 100 inclusive that, when multiplied with 2.55 and rounded to the closest integer (rounding up if two values are equally close), equalsalpha, let rounded be that integer divided by 100.
  3. Otherwise, let rounded be alpha divided by 0.255 and rounded to the closest integer (rounding up if two values are equally close), divided by 1000.
  4. Return the result of serializing rounded as a <number>.
    Otherwise, return the result of serializing the given value as a <number>.

所以有人提交了 Patch 来修复这个问题,使其反向序列号字符串的时候可以得到同样的值:

if (colorHasAlpha) {
  result.appendLiteral(", ");

  double floatAlpha = 0;
  // See  section in CSS Object Model (CSSOM)
  // Why ceil, not round here is that we ommit decimals in CSSPropertyParser::parseColorParameters.
  for (int i = static_cast < int > (ceil(alpha() / 2.56)); i <= 100; i) {
    int computedIntAlpha = static_cast < int > (i * nextafter(2.56, 0.0));
    if (computedIntAlpha == alpha()) {
      floatAlpha = i * 0.01;
      break;
    }
    if (computedIntAlpha > alpha())
      break;
  }

  if (!floatAlpha)
    floatAlpha = ceil(alpha() / nextafter(0.256, 0.0)) * 0.001;

  NumberToStringBuffer buffer;
  const char * alphaString = numberToFixedPrecisionString(alpha() / 255.0 f, 6, buffer, true);
  //const char * alphaString = numberToFixedPrecisionString(floatAlpha, 6, buffer, true);
  result.append(alphaString, strlen(alphaString));
}

更多

特别鸣谢 @kyriosli 对 C++ 代码的帮助٩(๑ᵒ̴̶̷͈᷄ᗨᵒ̴̶̷͈᷅)و 。

更多讨论请移步知乎:http://www.zhihu.com/question/26248345/answer/45773454

@antife-yinyue
Copy link

٩(๑ᵒ̴̶̷͈᷄ᗨᵒ̴̶̷͈᷅)و

@zhy1stgg
Copy link

好高深,膜拜一下

@kirk-ye
Copy link

kirk-ye commented Apr 24, 2015

NB

@wangjeaf
Copy link

👍

@maxming2333
Copy link

研究问题就是要这么深刻才行~

@yreenchan
Copy link

还可以研究得这么深入,学习一下!

@xiaoqiang730730
Copy link

还是姐姐看到远啊

@leoxoocanada
Copy link

一丝姐姐人气好高

@kyriosli
Copy link

ysjj人气好高

@MurphyL
Copy link

MurphyL commented May 15, 2015

真够深入的~~

@iPonyo
Copy link

iPonyo commented May 15, 2015

牛逼带闪电,佩服!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests