【问题标题】:Two unusual PHP operators used together to get image pixel color, please explain两个不寻常的PHP运算符一起使用来获取图像像素颜色,请解释
【发布时间】:2012-07-19 22:34:49
【问题描述】:

PHP imagecolorat() 函数可用于获取图像像素的 RGB 值,如文档中所示:

$im = imagecreatefrompng("php.png");
$rgb = imagecolorat($im, 10, 15);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;

我根本看不懂最后三行;我知道它们返回正确的值,但我无法弄清楚 >>& 运算符如何协同工作。有人可以解释一下吗?

作为参考,当$rgb = 9750526时,RGB值变成(148,199,254)

感谢您的回复。

【问题讨论】:

标签: php gd rgb


【解决方案1】:

& 和 >> 是两个不同的 bitwise operators (Bitwise operators in PHP)。它们使用值的位表示,这为我们提供了一些有用(而且快速!)的可能性。

>>right shift。如果您右移一个值,您将在表示中向右移动所有位。如果您有“11100”并将其右移两个位置,则剩下“111”(右端移出的位消失)。 >>右侧的数字表示要移位的位数。

&bitwise and。如果您有两个值表示为 111000 和 101011,并决定按位计算它们,那么您最终将得到 101000(两个值中设置相同位的位将为 1,否则为 0)。

我们现在有了理解上述代码所需的东西。 $rgb 包含一个整数值(该值不感兴趣,但位模式是),它表示 24 位(RGBA 为 32 位,但我们将在这里忽略它)。该值由红色的 8 位、绿色的 8 位和蓝色的 8 位组成。然而,我们对这 24 位一起代表什么数字不感兴趣,而是对单独的 R、G 和 B 值是什么感兴趣。

我们的值代表颜色的以下位:

rrrrrrrr gggggggg bbbbbbbb 

如果我们用 >> 16 移动这个位掩码,我们会得到:

                  rrrrrrrr

这就是 >> 16 所做的,只剩下代表红色的位。由于我们现在将这些位一直向下移动,该值表示 [0, 255] 中的一个数字。然后我们添加 & 255(这里用十六进制表示为 0xFF)以删除我们感兴趣的 8 以上的任何杂散位。当我们查看如何获得 G 值时,我们将了解现在是如何发生的:

我们的价值依然勇敢地代表着不同的颜色:

rrrrrrrr gggggggg bbbbbbbb 

如果我们用 >> 8 将此位掩码移动 8 位,我们将得到:

         rrrrrrrr gggggggg

但是等等!这不仅是代表 g 的位,还有代表 r 的位!这不是我们追求的值(因为我们只想要 g)。在前面的例子中我们很幸运,因为我们没有设置任何高于 r 的位,但是这次我们真的完成了我们的工作。但是我们知道 &,现在是时候真正了解它了。

我们现在有:

         rrrrrrrr gggggggg

然后我们应用 & 255(在您的代码中为 0xFF),它以位值表示为:

         00000000 11111111

由于 & 只保留在两个操作数中设置的位,我们最终将只得到 g 值:

                  gggggggg

移位值中表示红色的位现在为 0,我们只剩下表示原始颜色的绿色的位。由于它们已经移位到表示 [0, 255] 的相同 8 位,我们再次获得了我们的值。

最后一个值更容易,因为它已经位于我们想要的位置:在表示 [0, 255] 的位中。我们可以在这里做 & 操作,然后从另一边出去:

rrrrrrrr gggggggg bbbbbbbb & 
00000000 00000000 11111111

最后是:

                  bbbbbbbb

我们也有我们的蓝色值。

希望能解释清楚!

【讨论】:

  • 那么第一个& 0xFF是不必要的吗?
  • @Isius 取决于(这就是您通常想要包含它的原因)。如果 PNG 具有 32 位颜色值,则第一个字节将是 alpha 通道 - 如果您不执行& 0xFF,您最终将得到两个字节而不是单个字节。在rrrrrrrr 之前使用aaaaaaaaa 位扩展示例。
【解决方案2】:

>> 表示按位右移,因此位向右移动 n(分别为 16 和 8)个位置:

R = 1 字节 = 8 位

RRGGBB >> 16  =  0000RR
RRGGBB >> 8   =  00RRGG

按位与运算& 用于删除我们不希望在结果中出现的剩余内容,方法是使用除应保留的部分之外的所有位置均为零的掩码:

00RRGG & 0000FF = 0000GG 

【讨论】:

    【解决方案3】:

    让我们试试吧:

    $rgb = 9750526;
    var_dump(sprintf('%032b', $rgb));
    

    $rgb = 00000000100101001100011111111110 //整数是 32 或 64 位 - b 部分是最后 8 位

    你只需要第三、第二和第一字节,所以>>(移位)它们 你得到二进制文件

    $rgb >> 16 =  string(32) "00000000000000000000000010010100" //r part is last 8 bits
    $rgb >> 8 =  string(32) "00000000000000001001010011000111" //g part is last 8 bits
    

    & 0xFF 是和 11111111 操作,结果你得到

    r = 10010100
    g = 11000111
    b = 11111110
    

    我希望那些是 (148,199,254) ;)

    【讨论】:

      【解决方案4】:

      我猜第一次出现 >>16 将所有位向右移动,直到只剩下红色,然后用 &0xFF 切断数字的开头。虽然我无法判断该示例中红色的位深度(我希望颜色是 3 个字节的倍数),但它有点像这样:

      rrggbb = 125438 // input
      >> 16 // shift right (might be 8 for this example)
      0000rr = 000012 // result
      & 0xFF // cut beginning
      rr = 12
      

      其他颜色也类似。

      【讨论】:

      • 移位操作的结果实际上是0000rr - 左侧移入零。
      【解决方案5】:

      imageColorAt() 返回一个包含红色、绿色和蓝色值的 24 位数字。红色值乘以 2^16,绿色乘以 2^8,蓝色保持“原样”。

      在十六进制中,您的值为 0x94C7FE;通过将其右移 16 位,您会丢失最后四个半字节,并留下 0x94。 “& 0xFF”取最后 8 位(因此是多余的)。

      通常这个 shift'n'cut 是这样完成的:

      $value = imageColorAt(...)   // Gets 94C7FE
      $blue  = $value & 0xFF;       // Takes the last "FE" (254 in decimal)
      $value >>= 8;                 // Shifts right, 94C7 remains
      $green = $value & 0xFF;       // Takes "C7"
      $value >>= 8;                 // Shifts right, 94 remains
      $red   = $value;              // Takes "94" hex, which is 148 dec.
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-04-18
        • 2015-01-19
        • 1970-01-01
        • 1970-01-01
        • 2016-11-15
        • 1970-01-01
        • 2013-07-21
        • 1970-01-01
        相关资源
        最近更新 更多